From c7ced6eae00172fdb38dd7eac42c2106577f4174 Mon Sep 17 00:00:00 2001 From: zhaochengke Date: Fri, 5 May 2023 11:55:25 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=BC=E5=90=88=E5=A1=AB=E6=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + src/api/sunVillage_info/entity/report.js | 181 ++++++ src/components/form/FieldDatePicker.vue | 64 +- src/router/index.js | 18 + src/utils/expression.js | 512 +++++++++++++++ src/utils/index.js | 11 + src/utils/utils.js | 4 + .../entityReport/reportList.vue | 278 ++++++++ .../entityReport/reportView.vue | 595 ++++++++++++++++++ 9 files changed, 1658 insertions(+), 7 deletions(-) create mode 100644 src/api/sunVillage_info/entity/report.js create mode 100644 src/utils/expression.js create mode 100644 src/views/sunVillage_info/entityReport/reportList.vue create mode 100644 src/views/sunVillage_info/entityReport/reportView.vue diff --git a/.gitignore b/.gitignore index 541a820f..00ae9620 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ yarn-error.log* *.ntvs* *.njsproj *.sln + +dist.* diff --git a/src/api/sunVillage_info/entity/report.js b/src/api/sunVillage_info/entity/report.js new file mode 100644 index 00000000..fa444636 --- /dev/null +++ b/src/api/sunVillage_info/entity/report.js @@ -0,0 +1,181 @@ +import request from '@/utils/request' + +// 查询统计报列表 +export function listReporttitle(query) { + return request({ + url: '/entity/reporttitle/list', + method: 'get', + params: query + }) +} + +// 导出统计报 +export function exportReporttitle(query) { + return request({ + url: '/entity/reporttitle/export', + method: 'get', + params: query + }) +} + +// 查询统计报详细 +export function getReporttitle(id) { + return request({ + url: '/entity/reporttitle/get/' + id, + method: 'get' + }) +} + +// 新增统计报 +export function addReporttitle(data) { + return request({ + url: '/entity/reporttitle/add', + method: 'post', + data: data + }) +} + +// 修改统计报 +export function updateReporttitle(data) { + return request({ + url: '/entity/reporttitle/edit', + method: 'post', + data: data + }) +} + +// 删除统计报 +export function delReporttitle(id) { + return request({ + url: '/entity/reporttitle/remove/' + id, + method: 'get' + }) +} + +// 启用统计报 +export function enableReporttitle(id) { + return request({ + url: '/entity/reporttitle/enable/' + id, + method: 'post' + }) +} + +// 禁用统计报 +export function disableReporttitle(id) { + return request({ + url: '/entity/reporttitle/disable/' + id, + method: 'post' + }) +} + +// 获取模板 +export function getReportTemplate(id) { + return request({ + url: '/entity/reporttitle/template/' + id, + method: 'get' + }) +} + +// 获取横向模板 +export function getReportHorizontalTemplate(id) { + return request({ + url: '/entity/reporttitle/horizontalTemplate/' + id, + method: 'get' + }) +} + +// 查询统计填报列表 +export function listReport(query) { + return request({ + url: '/entity/report/list', + method: 'get', + params: query + }) +} + +// 导出统计填报 +export function exportReport(query) { + return request({ + url: '/entity/report/export', + method: 'get', + params: query + }) +} + +// 查询统计填报详细 +export function getReport(id) { + return request({ + url: '/entity/report/get/' + id, + method: 'get' + }) +} + +// 新增统计填报 +export function addReport(data) { + return request({ + url: '/entity/report/add', + method: 'post', + data: data + }) +} + +// 修改统计填报 +export function updateReport(data) { + return request({ + url: '/entity/report/edit', + method: 'post', + data: data + }) +} + +// 删除统计填报 +export function delReport(id) { + return request({ + url: '/entity/report/remove/' + id, + method: 'get' + }) +} + +// 导出统计填报 +export function reportExport(id) { + return request({ + url: '/entity/report/reportExport/' + id, + method: 'get', + }) +} + +// 竖向统计 +export function summaryStatistics(query) { + return request({ + url: '/entity/report/summaryStatistics', + method: 'get', + params: query + }) +} + +// 导出竖向统计 +export function summaryStatisticsExport(query) { + return request({ + url: '/entity/report/summaryStatisticsExport', + method: 'get', + params: query + }) +} + +// 横向统计 +export function horizontalSummaryStatistics(query) { + return request({ + url: '/entity/report/horizontalSummaryStatistics', + method: 'get', + params: query + }) +} + +// 导出横向统计 +export function horizontalSummaryStatisticsExport(query) { + return request({ + url: '/entity/report/horizontalSummaryStatisticsExport', + method: 'get', + params: query + }) +} diff --git a/src/components/form/FieldDatePicker.vue b/src/components/form/FieldDatePicker.vue index db34c8a5..5ed1cbfb 100644 --- a/src/components/form/FieldDatePicker.vue +++ b/src/components/form/FieldDatePicker.vue @@ -14,18 +14,33 @@ :rules="rules" :required="required" :label-width="labelWidth || 'auto'" + :input-align="inputAlign || 'left'" > - + + + + + import { formatDate } from "element-ui/src/utils/date-util.js" +import {strtotime} from "@/utils"; export default { name: "fieldDatePicker", props: [ - 'name', 'readonly', 'value', 'label', 'placeholder', 'required', 'rules', 'labelWidth', - 'type', // 类型, 仅支持 datetime date time year-month month-day datehour + 'name', 'readonly', 'value', 'label', 'placeholder', 'required', 'rules', 'labelWidth', 'inputAlign', + 'type', // 类型, 仅支持 datetime date time year-month month-day datehour, 额外支持year(但formatter必须为yyyy, 可以提供yearRangeLength=指定年范围) 'formatter', // value的格式化 String|Function|undefined 字符串为格式字符串, 函数则必须有返回 undefined则不转换 'clearable', // 点击取消时清空绑定值 + 'yearRangeLength', // type === 'year' 时生成的年份数量范围 [YEAR - yearRangeLength, YEAR + yearRangeLength] ], watch: { value: function (newVal, oldVal) { @@ -75,7 +92,7 @@ export default { openPopup() { if(!this.readonly) { - console.log(this.internalValue); + //console.log(this.internalValue); this.popupVisible = true; this.$nextTick(() => { try @@ -83,8 +100,9 @@ export default { if(1) { let values = (this.visibleValue || this.getValue(new Date)).split(/\D+/); //TODO: 按非数字符号粗略分割解析初始值, 仅对于类似yyyy-MM-dd - console.log(values); - this.$refs.picker.getPicker().setValues(values); + //console.log(values); + let picker = this.$refs.picker.getPicker ? this.$refs.picker.getPicker() : this.$refs.picker; + picker.setValues(values); } else { //TODO: 打开时保存初始值, 取消或点击遮罩未确定的时候恢复该初始值到v-model @@ -132,7 +150,39 @@ export default { this.internalValue = data; this.visibleValue = this.getValue(data); }, + genYearColumns(num) { + let d; + try + { + if(this.value) + { + d = strtotime(this.value, 'yyyy'); + } + else + d = new Date; + } + catch(e) + { + d = new Date; + } + let y = d.getFullYear(); + let arr = ['' + y]; + for(let i = 1; i <= num; i++) + { + arr.push('' + (y + i)); + arr.splice(0, 0, '' + (y - i)); + } + return arr; + }, + strtotime, }, + computed: { + yearColumns() { + if(this.type !== 'year') + return []; + return this.genYearColumns(this.yearRangeLength || 100); + }, + } } diff --git a/src/router/index.js b/src/router/index.js index ee1af1f2..6aa5ccd0 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -3444,6 +3444,24 @@ export const constantRoutes = [ }, component: (resolve) => require(['@/views/sunVillage_info/list_register_detail'], resolve) }, + { // 综合填报 + path: '/sunVillage_info/entityReportList', + name: 'entityReportList', + meta: { + title: '综合填报', + hidden: true, + }, + component: (resolve) => require(['@/views/sunVillage_info/entityReport/reportList'], resolve) + }, + { // 综合填报 + path: '/sunVillage_info/entityReportView', + name: 'entityReportView', + meta: { + title: '综合填报', + hidden: true, + }, + component: (resolve) => require(['@/views/sunVillage_info/entityReport/reportView'], resolve) + }, { // 统计填报 path: '/sunVillage_info/statistical_report', name: 'sunVillageInfoStatisticalReport', diff --git a/src/utils/expression.js b/src/utils/expression.js new file mode 100644 index 00000000..09cd7507 --- /dev/null +++ b/src/utils/expression.js @@ -0,0 +1,512 @@ + +export const Expression = function(_str, _flag = 0, _placeholderValueMap = {}) +{ + this.pos = 0; // 当前解析表达式字符串的位置 + this.str = _str; // 表达式 + this.errno = Expression.NO_ERROR; // 当前错误码 + this.placeholderValueMap = _placeholderValueMap; // 占位符值字典 + this.flag = _flag | Expression.PARSE_FLAG_ERROR_HANDLE_SCHEME_THROW_EXCEPTION; // 行为标记 + this.placeholders = []; +} + +Expression.NO_ERROR = 0; +Expression.ERROR = 1; +Expression.NULL_STRING = 2; +Expression.BY_ZERO = 3; +Expression.MISSING_DIGIT = 4; +Expression.MISSING_RIGHT_PARENT = 5; +Expression.MISSING_OPERATOR = 6; +Expression.INVALID_CHARACTER = 7; +Expression.MISSING_PLACEHOLDER = 8; +Expression.INVALID_DIGIT = 9; +Expression.INVALID_PLACEHOLDER = 10; +Expression.MISSING_RIGHT_PLACEHOLDER_END = 11; +Expression.INVALID_PLACEHOLDER_FUNCTION = 12; +Expression.MISSING_COMPARE_OPERATOR = 13; +Expression.INVALID_COMPARE_OPERATOR = 14; +Expression.INCOMPLETE = 15; +Expression.ErrorStr = [ + "", + "错误", + "空字符串", + "除0", + "缺失数字", + "缺失右括号", + "缺失运算符", + "无效字符", + "缺失占位符", + "无效数字", + "无效占位符", + "缺失占位符结尾标识", + "无效占位符内置函数", + "缺失比较运算符", + "无效比较运算符", + "表达式不完整", +]; + +Expression.PARSE_FLAG_NONE = 0; +Expression.PARSE_FLAG_ERROR_HANDLE_SCHEME_RETURN_ZERO = 1; // 解析到错误则当前部分返回0 +Expression.PARSE_FLAG_ERROR_HANDLE_SCHEME_THROW_EXCEPTION = 1 << 1; // 解析到错误则抛出异常 +Expression.PARSE_FLAG_ALL_NUMBER_AS_PLACEHOLDER = 1 << 3; // 所有数字都作为占位符 +Expression.PARSE_FLAG_IGNORE_ERROR_MISSING_PLACEHOLDER = 1 << 4; // 忽略占位符缺失 +Expression.PARSE_FLAG_IGNORE_ERROR_BY_ZERO = 1 << 5; // 忽略除0 + +// 是否读取到字符串结尾 +Expression.prototype.__EOF = function() +{ + return !this.str || this.pos >= this.str.length; +}; + +// 字符串转为数字 +Expression.prototype.__StrTo = function(numStr) +{ + return Number(numStr) +}; + +// 开始解析, 重置状态 +Expression.prototype.__Ready = function() +{ + this.pos = 0; + this.errno = Expression.NO_ERROR; + this.placeholders = []; + let err = this.__CheckStr(); + return (err === Expression.NO_ERROR); +}; + +// 解析 值 括号 占位符 的优先级最高的表达式 +Expression.prototype.__EvalV = function() +{ + if(this.__EOF()) + { + return this.__FatalError(Expression.INCOMPLETE); + } + this.__SkipBlank(); + let nStr = this.str.substring(this.pos); + let p = 0; + let nag = false; + let result = 0; + if(nStr.charAt(p) === '-') + { + nag = true; + this.pos++; + p++; + } + p += this.__SkipBlank(); + if(nStr.charAt(p) === '(') // 括号表达式 + { + p++; + this.pos++; + result = this.__EvalP(); + if(this.str.charAt(this.pos) !== ')') + { + return this.__FatalError(Expression.MISSING_RIGHT_PARENT); + } + this.pos++; + } + else if(nStr.charAt(p) === '{') // 占位符 + { + p++; + this.pos++; + p += this.__SkipBlank(); + let d = ''; + let size = 0; + let ch = nStr.charAt(p); + while("`~@#%^&*()-+=,/?:;\"'|\\[{]}".indexOf(ch) === -1) // 中文支持 + { + size++; + d += ch; + p++; + this.pos++; + if(p >= nStr.length) + { + return this.__FatalError(Expression.MISSING_RIGHT_PLACEHOLDER_END); + } + ch = nStr.charAt(p); + } + p += this.__SkipBlank(); + if(this.str.charAt(this.pos) !== '}') + { + return this.__FatalError(Expression.MISSING_RIGHT_PLACEHOLDER_END); + } + this.pos++; + if(size > 0) + { + result = this.__ParsePlaceholder(d); + } + else + { + return this.__NormalError(Expression.MISSING_PLACEHOLDER); + } + } + else // 纯数字 + { + let d = ''; + let size = 0; + let ch = nStr.charAt(p); + while((ch >= '0' && ch <= '9') || ch === '.') + { + size++; + d += ch; + p++; + this.pos++; + if(p >= nStr.length) + break; + ch = nStr.charAt(p); + } + if(size > 0) + { + let m = d.indexOf("."); + if(m !== -1) + { + let n = d.lastIndexOf("."); + if(n !== -1 && m !== n) + { + return this.__FatalError(Expression.INVALID_DIGIT); + } + if(m === 0 || m === d.length - 1) + d = d.substr(0, m) + d.substr(m + 1); + } + if(this.flag & Expression.PARSE_FLAG_ALL_NUMBER_AS_PLACEHOLDER) + result = this.__ParsePlaceholder(d); + else + result = this.__StrTo(d); + } + else + { + return this.__FatalError(Expression.MISSING_DIGIT); + } + } + if(nag) + result = -result; + return result; +}; + +// 解析* /的优先级相对较高的表达式 +Expression.prototype.__EvalM = function() +{ + if(this.__EOF()) + { + return this.__FatalError(Expression.INCOMPLETE); + } + let first = this.__EvalV(); + let result = first; + this.__SkipBlank(); + let nStr = this.str.substring(this.pos); + while(nStr.length > 0) + { + let ch = nStr.charAt(0); + if(ch !== '*' && ch !== '/') + return result; + else + { + this.pos++; + let second = this.__EvalV(); + if(ch === '*') + result = result *second; + else + { + if(second === 0) + { + return this.__NormalError(Expression.BY_ZERO); + } + result = result / second; + } + } + nStr = this.str.substring(this.pos); + } + return result; +}; + +// 解析+ -的优先级最低的表达式 +Expression.prototype.__EvalP = function() +{ + if(this.__EOF()) + { + return this.__FatalError(Expression.INCOMPLETE); + } + this.__SkipBlank(); + let first = this.__EvalM(); + let result = first; + let nStr = this.str.substring(this.pos); + while(nStr.length > 0) + { + let ch = nStr.charAt(0); + if(ch !== '+' && ch !== '-') + return result; + else + { + this.pos++; + let second = this.__EvalM(); + if(ch === '+') + result = result + second; + else + result = result - second; + } + nStr = this.str.substring(this.pos); + } + return result; +}; + +// 解析比较运算符 +Expression.prototype.__EvalCmp = function() +{ + if(this.__EOF()) + { + return this.__FatalError(Expression.MISSING_COMPARE_OPERATOR); + } + this.__SkipBlank(); + let result = ''; + let nStr = this.str.substring(this.pos); + while(nStr.length > 0) + { + let ch = nStr.charAt(0); + if(ch !== '=' && ch !== '!' && ch !== '>' && ch !== '<') + break; + else + { + if(result.length === 0) + { + result += ch; + this.pos++; + } + else // === 1 + { + if(result !== "=") // 如果第一个字符不是=, 第二个字符必定是= + { + if(ch !== '=') + return this.__FatalError(Expression.INVALID_COMPARE_OPERATOR); + } + else // 如果第一个字符是=, 第二个字符可以是= + { + if(ch === '=') + result += ch; + else + { + break; + } + } + result += ch; + this.pos++; + } + } + if(result.length >= 2) + break; + nStr = this.str.substring(this.pos); + } + + if(result === "=") + result = "=="; + return result; +}; + +// 开始计算 +Expression.prototype.Calc = function() +{ + if(!this.__Ready()) + return this.__OnError(0); + return this.__EvalP(); +}; + +// 判断表达式断言 +Expression.prototype.Predicate = function() +{ + if(!this.__Ready()) + return this.__OnError(false); + let a = this.__EvalP(); + if(this.HasError()) + return this.__OnError(false); + let op = this.__EvalCmp(); + if(this.HasError()) + return this.__OnError(false); + let b = this.__EvalP(); + if(this.HasError()) + return this.__OnError(false); + //console.log(a + op + b); + return eval(a + op + b); +}; + +Expression.prototype.FLAG = function(flags) +{ + for(let i of arguments) + { + if((this.flag & i) === i) + return true; + } + return false; +} + +// 普通错误, 仅设置可允许忽略的错误时调用, 错误被忽略时不设置错误码 +Expression.prototype.__NormalError = function(err) +{ + switch(err) + { + case Expression.BY_ZERO: + if(this.FLAG(Expression.PARSE_FLAG_IGNORE_ERROR_BY_ZERO)) + return 0; + break; + case Expression.MISSING_PLACEHOLDER: + if(this.FLAG(Expression.PARSE_FLAG_IGNORE_ERROR_MISSING_PLACEHOLDER)) + return 0; + break; + case Expression.INVALID_DIGIT: + if(this.FLAG(Expression.PARSE_FLAG_IGNORE_ERROR_INVALID_DIGIT)) + return 0; + break; + } + return this.__FatalError(err); +}; + +// 全局错误处理 +Expression.prototype.__OnError = function(ifError) +{ + if(this.FLAG(Expression.PARSE_FLAG_ERROR_HANDLE_SCHEME_THROW_EXCEPTION)) // 抛异常 + { + throw new Error(this.Error()); + } + else // 返回0 + { + return ifError; + } +}; + +// 计算是否有错误 +Expression.prototype.HasError = function() +{ + return this.errno !== Expression.NO_ERROR; +}; + +// 返回计算的错误, 并清除错误 +Expression.prototype.GetError = function() +{ + let e = this.errno; + this.errno = Expression.NO_ERROR; + return e; +} + +// 获取当前错误的字符串 +Expression.prototype.Error = function() +{ + return Expression.ErrorStr[this.errno]; +} + +// 致命错误 +Expression.prototype.__FatalError = function(err) +{ + this.errno = err; + throw new Error(this.Error()); +}; + +Expression.prototype.ParsedPlaceholders = function() +{ + return this.placeholders; +} + +// 解析占位符 +Expression.prototype.__ParsePlaceholder = function(name) +{ + this.placeholders.push(name); + if(!this.placeholderValueMap) + { + return this.__NormalError(Expression.MISSING_PLACEHOLDER); + } + if(name.startsWith("$")) // 特殊环境变量 + return this.__ParseNumber(this.placeholderValueMap[name]); + + let ptr = this.placeholderValueMap; + let d = null; + let notFetch = false; + let i = 0; + do + { + let index = name.indexOf(".", i); + let s = index === -1 ? name.substring(i) : name.substring(i, index); + i += s.length + 1; + s = s.trim(); + if(s.startsWith("$")) // 函数管线 + { + if(!notFetch) // 此时当前值应该是数字, 不再继续向下取Object/Map + { + d = this.__ParseNumber(ptr); // 当前值转换为数字 + notFetch = true; // 此时当前值应该是数字, 不再继续向下取Object/Map + } + switch(s.substring(1).toLowerCase()) + { + case "abs": + d = Math.abs(d); + break; + case "neg": + d = -d; + break; + case "lt0": + if(d >= 0) + return 0; + break; + case "gt0": + if(d <= 0) + return 0; + break; + default: + return this.__FatalError(Expression.INVALID_PLACEHOLDER_FUNCTION); + } + } + else + { + if(notFetch/*null != d*/) // TODO: 被管线函数处理过, 占位符表达式书写不应该出现该情况, 管线函数应都在表达式尾部, 并且不在表达式起始 + return d; + ptr = ptr[s]; + if(null === ptr || undefined === ptr) + { + return this.__NormalError(Expression.MISSING_PLACEHOLDER); + } + } + } while(i < name.length); + if(null == d) + return this.__ParseNumber(ptr); + else + return d; +}; + +// 检查表达式字符串 +Expression.prototype.__CheckStr = function() +{ + if(!this.str) + { + return Expression.NULL_STRING; + } + return Expression.NO_ERROR; +}; + +// 跳过空白符 +Expression.prototype.__SkipBlank = function() +{ + let i = 0; + for(; (this.pos + i) < this.str.length; i++) + { + if(!/\s/.test(this.str.charAt(this.pos + i))) + break; + } + this.pos += i; + return i; +}; + +// 对象转数字 +Expression.prototype.__ParseNumber = function(val) +{ + if(null === val) + return this.__NormalError(Expression.INVALID_DIGIT); + if(val instanceof Number) + return val; + else + { + try + { + return this.__StrTo(val.toString()); + } + catch(e) + { + console.error(e); + return this.__NormalError(Expression.INVALID_DIGIT); + } + } +}; + diff --git a/src/utils/index.js b/src/utils/index.js index c4b7904e..879b08bf 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1206,3 +1206,14 @@ export function manual_page(list, page_no = 1, page_size = 10) let start = (page_no - 1) * page_size; return list.slice(start, start + page_size); } + +export function array_toMap(arr, byFunc, overriding) +{ + let res = {}; + arr.forEach((x) => { + let a = byFunc(x); + if(!res.hasOwnProperty(a) || overriding) + res[a] = x; + }); + return res; +} diff --git a/src/utils/utils.js b/src/utils/utils.js index f4cb3c4e..5eae9686 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -158,3 +158,7 @@ export function isBankCard (str_cardNo) { return false; } } + +export function is_not_number(val) { + return (val === null || val === undefined || val === ''); +} diff --git a/src/views/sunVillage_info/entityReport/reportList.vue b/src/views/sunVillage_info/entityReport/reportList.vue new file mode 100644 index 00000000..fd581a4c --- /dev/null +++ b/src/views/sunVillage_info/entityReport/reportList.vue @@ -0,0 +1,278 @@ + + + + + diff --git a/src/views/sunVillage_info/entityReport/reportView.vue b/src/views/sunVillage_info/entityReport/reportView.vue new file mode 100644 index 00000000..67a0dd39 --- /dev/null +++ b/src/views/sunVillage_info/entityReport/reportView.vue @@ -0,0 +1,595 @@ + + + + +