diff --git a/wxmp/bin/pages.js b/wxmp/bin/pages.js index fb9f0d4..9e0d82a 100644 --- a/wxmp/bin/pages.js +++ b/wxmp/bin/pages.js @@ -6,7 +6,7 @@ const start = () => { easycom: { autoscan: true, custom: { - "^(K|V)(.*)": "@/components/$1$2.vue", + "^(Ai|V)(.*)": "@/components/$1$2.vue", "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" } }, diff --git a/wxmp/package.json b/wxmp/package.json index aeafa31..63f1e03 100644 --- a/wxmp/package.json +++ b/wxmp/package.json @@ -22,6 +22,10 @@ "@dcloudio/uni-mp-weixin": "3.0.0-3061420221215001", "@dcloudio/uni-quickapp-webview": "3.0.0-3061420221215001", "@dcloudio/uni-ui": "^1.4.23", + "axios": "^1.2.2", + "axios-miniprogram-adapter": "^0.3.5", + "dayjs": "^1.11.7", + "query-string": "^8.1.0", "vue": "^3.2.45", "vue-i18n": "^9.1.9" }, diff --git a/wxmp/src/components/AiGroup.vue b/wxmp/src/components/AiGroup.vue new file mode 100644 index 0000000..7ccd489 --- /dev/null +++ b/wxmp/src/components/AiGroup.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/wxmp/src/components/AiItem.vue b/wxmp/src/components/AiItem.vue new file mode 100644 index 0000000..85a8e4d --- /dev/null +++ b/wxmp/src/components/AiItem.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/wxmp/src/components/AiPagePicker.vue b/wxmp/src/components/AiPagePicker.vue new file mode 100644 index 0000000..b18ded1 --- /dev/null +++ b/wxmp/src/components/AiPagePicker.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/wxmp/src/components/pages/submitEvaluation.vue b/wxmp/src/components/pages/submitEvaluation.vue new file mode 100644 index 0000000..8c5828c --- /dev/null +++ b/wxmp/src/components/pages/submitEvaluation.vue @@ -0,0 +1,62 @@ + + + + + diff --git a/wxmp/src/main.js b/wxmp/src/main.js index 3889ada..9c7a8fa 100644 --- a/wxmp/src/main.js +++ b/wxmp/src/main.js @@ -1,8 +1,10 @@ import {createSSRApp} from "vue"; import App from "./App.vue"; +import util from "./utils/util"; export function createApp() { const app = createSSRApp(App); + Object.keys(util).map(e => app.config.globalProperties[e] = util[e]) return { app, }; diff --git a/wxmp/src/utils/coin.js b/wxmp/src/utils/coin.js new file mode 100644 index 0000000..11bb11d --- /dev/null +++ b/wxmp/src/utils/coin.js @@ -0,0 +1,19 @@ +export default { + cny(money) { + if (money) { + money.toLocaleString('zh-Hans-CN', {style: 'currency', currency: "CNY"}) + } + return money + }, + cn(money) { + let num = parseFloat(money), cnMoney = '', + units = '仟佰拾亿仟佰拾万仟佰拾元角分', + cnNum = '零壹贰叁肆伍陆柒捌玖' + num = num.toFixed(2).replace(/\./g, '') + units = units.substring(units.length - num.length) + Array.from(num).map((e, i) => { + cnMoney += cnNum.charAt(e) + units.charAt(i) + }) + return cnMoney.replace(/零角零分$/, '整').replace(/零[仟佰拾]/g, '零').replace(/零{2,}/g, '零').replace(/零([亿|万])/g, '$1').replace(/零+元/, '元').replace(/亿零{0,3}万/, '亿').replace(/^元/, "零元") + } +} diff --git a/wxmp/src/utils/dict.js b/wxmp/src/utils/dict.js new file mode 100644 index 0000000..a376716 --- /dev/null +++ b/wxmp/src/utils/dict.js @@ -0,0 +1,56 @@ +export default { + instance: null, + init(instance) { + this.instance = instance + }, + dicts() { + return uni.getStorageSync('dicts') || []; + }, + load(...code) { + return !!this.instance && this.instance.post('/admin/dictionary/queryValsByCodeList?codeList=' + code.join(','), null, { + withoutToken: true + }).then((res) => { + if (res && res.data) { + let cacheDicts = {}, + meta = {}; + this.dicts().map((e) => (cacheDicts[e.key] = e)); + res.data.map((e) => (meta[e.key] = e)); + let dicts = {...cacheDicts, ...meta}; + uni.setStorageSync('dicts', Object.values(dicts)); + } + }); + }, + getDict(key) { + if (this.dicts().length) { + let dict = this.dicts().find((e) => e.key == key); + return dict ? dict.values : []; + } else return []; + }, + getValue(key, label) { + if (this.dicts().length) { + let dict = this.dicts().find((e) => e.key == key); + if (dict) { + let item = dict.values.find((v) => v.dictName == label); + return item ? item.dictValue : label; + } else return label; + } else return label; + }, + getLabel(key, value) { + if (this.dicts().length) { + let dict = this.dicts().find((e) => e.key == key); + if (dict) { + let item = dict.values.find((v) => v.dictValue == value); + return item ? item.dictName : value; + } else return value ? value : ''; + } else return value ? value : ''; + }, + getColor(key, value) { + if (this.dicts().length) { + let dict = this.dicts().find((e) => e.key == key); + if (dict) { + let item = dict.values.find((v) => v.dictValue == value); + return item ? item.dictColor : value; + } else return value; + } else return value; + } +} diff --git a/wxmp/src/utils/http.js b/wxmp/src/utils/http.js new file mode 100644 index 0000000..6e85df0 --- /dev/null +++ b/wxmp/src/utils/http.js @@ -0,0 +1,29 @@ +import axios from 'axios' +import adapter from 'axios-miniprogram-adapter' + +const instance = axios.create({ + timeout: 600000, + withCredentials: true, + adapter +}) +const getToken = () => { + let vuex = uni.getStorageSync("vuex") + return !!vuex ? JSON.parse(vuex).token : null +} +const source = axios.CancelToken.source(); +instance.interceptors.request.use(config => { + if (config.withoutToken) { + return config + } else if (getToken()) { + config.headers["Authorization"] = getToken() + } else { + config.cancelToken = source.token + source.cancel("用户未验证,取消请求:" + config.url) + } + return config +}, err => { + console.error(err) + return Promise.reject(err) +}) + +export default instance diff --git a/wxmp/src/utils/identity.js b/wxmp/src/utils/identity.js new file mode 100644 index 0000000..2275168 --- /dev/null +++ b/wxmp/src/utils/identity.js @@ -0,0 +1,238 @@ +import dayjs from "./moment"; + + +const PARAMS = { + /* 省,直辖市代码表 */ + provinceAndCitys: { + 11: '北京', + 12: '天津', + 13: '河北', + 14: '山西', + 15: '内蒙古', + 21: '辽宁', + 22: '吉林', + 23: '黑龙江', + 31: '上海', + 32: '江苏', + 33: '浙江', + 34: '安徽', + 35: '福建', + 36: '江西', + 37: '山东', + 41: '河南', + 42: '湖北', + 43: '湖南', + 44: '广东', + 45: '广西', + 46: '海南', + 50: '重庆', + 51: '四川', + 52: '贵州', + 53: '云南', + 54: '西藏', + 61: '陕西', + 62: '甘肃', + 63: '青海', + 64: '宁夏', + 65: '新疆', + 71: '台湾', + 81: '香港', + 82: '澳门', + 91: '国外' + }, + + /* 每位加权因子 */ + powers: [ + '7', + '9', + '10', + '5', + '8', + '4', + '2', + '1', + '6', + '3', + '7', + '9', + '10', + '5', + '8', + '4', + '2' + ], + + /* 第18位校检码 */ + parityBit: ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'], + + /* 性别 */ + genders: {1: '男', 0: '女'}, +} + +const Check = { + /* 校验地址码 */ + checkAddressCode(addressCode) { + const check = /^[1-9]\d{5}$/.test(addressCode) + if (!check) return false + return !!PARAMS.provinceAndCitys[parseInt(addressCode.substring(0, 2))] + }, + /* 校验日期码 */ + checkBirthDayCode(birDayCode) { + const check = /^[1-9]\d{3}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))$/.test( + birDayCode + ) + if (!check) return false + const yyyy = parseInt(birDayCode.substring(0, 4), 10) + const mm = parseInt(birDayCode.substring(4, 6), 10) + const dd = parseInt(birDayCode.substring(6), 10) + const xdata = new Date(yyyy, mm - 1, dd) + if (xdata > new Date()) { + return false // 生日不能大于当前日期 + } else { + return ( + xdata.getFullYear() == yyyy && + xdata.getMonth() == mm - 1 && + xdata.getDate() == dd + ) + } + }, + /* 验证校检码 */ + checkParityBit(idCardNo) { + const parityBit = idCardNo.charAt(17).toUpperCase() + return this.getParityBit(idCardNo) == parityBit + }, +// 校验15位的身份证号码 + check15IdCardNo(idCardNo) { + // 15位身份证号码的基本校验 + let check = /^[1-9]\d{7}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))\d{3}$/.test( + idCardNo + ) + if (!check) return false + // 校验地址码 + const addressCode = idCardNo.substring(0, 6) + check = this.checkAddressCode(addressCode) + if (!check) return false + const birDayCode = '19' + idCardNo.substring(6, 12) + // 校验日期码 + return this.checkBirthDayCode(birDayCode) + }, + +// 校验18位的身份证号码 + check18IdCardNo(idCardNo) { + // 18位身份证号码的基本格式校验 + let check = /^[1-9]\d{5}[1-9]\d{3}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))\d{3}(\d|x|X)$/.test( + idCardNo + ) + if (!check) return false + // 校验地址码 + const addressCode = idCardNo.substring(0, 6) + check = this.checkAddressCode(addressCode) + if (!check) return false + // 校验日期码 + const birDayCode = idCardNo.substring(6, 14) + check = this.checkBirthDayCode(birDayCode) + if (!check) return false + // 验证校检码 + return this.checkParityBit(idCardNo) + }, + /* 计算校检码 */ + getParityBit(idCardNo) { + const id17 = idCardNo.substring(0, 17) + /* 加权 */ + let power = 0 + for (let i = 0; i < 17; i++) { + power += parseInt(id17.charAt(i), 10) * parseInt(PARAMS.powers[i]) + } + /* 取模 */ + const mod = power % 11 + return PARAMS.parityBit[mod] + } +} + +export default class Identity { + constructor(code) { + this.code = this.getId18(code) + this.init() + } + + init() { + const {code} = this + if (!!code) { + this.hideCode = Identity.hideId(code) + this.getIdCardInfo(code) + } + } + + static hideId(code) { + return code?.replace(/^(\d{10})\d{4}(.{4}$)/g, `$1${Array(5).join('*')}$2`) + } + + getId18(code) { + if (code.length == 15) { + const id17 = code.substring(0, 6) + '19' + code.substring(6) + const parityBit = Check.getParityBit(id17) + return id17 + parityBit + } else if (code.length == 18) { + return code + } else { + return null + } + } + + getIdCardInfo(idCardNo) { + this.birthday = Identity.getBirthday(idCardNo) + this.sex = Identity.getSex(idCardNo) + this.gender = PARAMS.genders[this.sex] + this.age = Identity.getAge(idCardNo) + this.areaId = Identity.getArea(idCardNo) + } + + /** + * 获取性别 + * @param code + * @returns {string} + */ + static getSex(code) { + return (Number(code.substring(16, 17)) % 2).toString() + } + + /** + * 获取年龄 + * @param code + * @returns {number} + */ + static getAge(code) { + const birthday = dayjs(code.substring(6, 14), 'YYYYMMDD') + return Math.ceil(dayjs.duration(dayjs().unix() - dayjs(birthday).unix(), 's').asYears()) + } + + /** + * 获取地区编码 + * @param code + */ + static getArea(code) { + return code.substring(0, 6) + new Array(7).join(0) + } + + /** + * 获取生日 + */ + static getBirthday(code) { + return dayjs(code.substring(6, 14), 'YYYYMMDD').format("YYYY-MM-DD").replace('Invalid Date', '') + } + + /* 校验15位或18位的身份证号码 */ + static check(idCardNo) { + // 15位和18位身份证号码的基本校验 + const check = /^\d{15}|(\d{17}(\d|x|X))$/.test(idCardNo) + if (!check) return false + // 判断长度为15位或18位 + if (idCardNo.length == 15) { + return Check.check15IdCardNo(idCardNo) + } else if (idCardNo.length == 18) { + return Check.check18IdCardNo(idCardNo) + } else { + return false + } + } +} diff --git a/wxmp/src/utils/moment.js b/wxmp/src/utils/moment.js new file mode 100644 index 0000000..a157d57 --- /dev/null +++ b/wxmp/src/utils/moment.js @@ -0,0 +1,35 @@ +import moment from 'dayjs' +import duration from 'dayjs/plugin/duration' +import updateLocale from 'dayjs/plugin/updateLocale' +import customParseFormat from 'dayjs/plugin/customParseFormat' +import 'dayjs/locale/zh-cn' + +moment.locale('zh-cn') +moment.extend(updateLocale) +moment.extend(customParseFormat) +moment.extend(duration) +moment.updateLocale('zh-cn', { + weekdays: "星期日|星期一|星期二|星期三|星期四|星期五|星期六".split("|"), + meridiem(hour) { + let word = "" + if (hour < 6) { + word = "凌晨" + } else if (hour < 9) { + word = "早上" + } else if (hour < 12) { + word = "上午" + } else if (hour < 14) { + word = "中午" + } else if (hour < 17) { + word = "下午" + } else if (hour < 19) { + word = "傍晚" + } else if (hour < 22) { + word = "晚上" + } else { + word = "夜里" + } + return word; + } +}) +export default moment diff --git a/wxmp/src/utils/regular.js b/wxmp/src/utils/regular.js new file mode 100644 index 0000000..b85a693 --- /dev/null +++ b/wxmp/src/utils/regular.js @@ -0,0 +1,15 @@ +export default { + phone: /^((0\d{2,3}-\d{7,8})|((13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}))$/, + password: /^(?=.*\d)(?=.*[a-zA-Z])(?=.*[~!@#$%^&*,.?_-])[\da-zA-Z~!@#$%^&*,.?_-]{8,16}$/, + money: /^([1-9]\d*|0)(\.\d{1,2})?$/, + area: { + village: /^\d{9}[^0]0{0,2}$/, + town: /^\d{6}[^0]0{0,2}000$/, + country: /^\d{4}[^0]0?0{6}$/, + city: /^\d{2}[^0]0?0{8}$/, + province: /^[^0]0?0{10}$/, + }, + zh: /^[\u4e00-\u9fa5]+$/, + email: /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/, + ip: /((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))/ +} diff --git a/wxmp/src/utils/util.js b/wxmp/src/utils/util.js new file mode 100644 index 0000000..22a14ce --- /dev/null +++ b/wxmp/src/utils/util.js @@ -0,0 +1,140 @@ +import $dayjs from './moment' +import $dict from './dict' +import $qs from 'query-string' +import $coin from './coin' +import $reg from "./regular" +import identity from "./identity" + +const $toast = (obj) => { + let params = {title: '', duration: 2000, icon: 'none'}; + if (typeof obj == 'string') { + params.title = obj; + } else { + Object.assign(params, obj); + } + uni.showToast(params); +}; + +const $loading = (title = "加载中") => { + uni.showLoading({ + title: title || '加载中', + mask: true + }); +}; + +const $hideLoading = () => { + uni.hideLoading(); +}; + +const $dialog = { + alert: (params) => { + return new Promise((resolve) => { + uni.showModal({ + title: '温馨提示', + showCancel: false, + confirmColor: '#197DF0', + confirmText: params?.confirmButtonText || '确定', + ...params, + success: (res) => { + if (res.confirm) { + resolve(); + } + } + }); + }); + }, + + confirm: (params) => { + return new Promise((resolve, reject) => { + uni.showModal({ + title: '温馨提示', + showCancel: true, + confirmColor: '#197DF0', + cancelText: params.cancelButtonText ? params.cancelButtonText : '取消', + confirmText: params.confirmButtonText ? params.confirmButtonText : '确定', + ...params, + success: (res) => { + if (res.confirm) { + resolve(); + } else if (res.cancel) { + reject(); + } + } + }); + }); + } +}; + +const $linkTo = (url) => { + uni.navigateTo({ + url + }); +}; + +const $formatName = (name) => { + if (name === undefined) { + return; + } + return name.substr(name.length - 2, name.length > 2 ? name.length - 1 : name.length); +}; + +const $previewImage = (list, index, urlName) => { + uni.previewImage({ + current: list[index][urlName], + urls: list.map((v) => v[urlName]) + }); +}; + + +const $getUserProfile = () => { + return new Promise(function (resolve) { + wx.getUserProfile({ + desc: '用于完善会员资料', + lang: 'zh_CN', + success: (data) => { + resolve(data); + }, + fail: (err) => { + console.log(err); + } + }); + }); +}; +/** + * 获取code + * @returns {Promise} + */ +const $getLoginCode = () => { + return new Promise(function (resolve, reject) { + uni.login({ + success: function (res) { + if (res.code) { + resolve(res); + } else { + reject(res); + } + }, + fail: function () { + reject(false); + } + }); + }); +}; + +export default { + $toast, + $loading, + $hideLoading, + $dialog, + $linkTo, + $formatName, + $previewImage, + $getUserProfile, + $dayjs, + $dict, + $getLoginCode, + $qs, + $coin, + $reg, + $ChID: identity +};