@ -0,0 +1,5 @@ |
|||
ENV='development' |
|||
# base api |
|||
VITE_APP_BASE_URL = 'https://evote.truescloud.com' |
|||
VITE_APP_BASE_PRE = '/dev-api' |
|||
VITE_APP_BASE_NAME = 'POS' |
|||
@ -0,0 +1,6 @@ |
|||
ENV='production' |
|||
# base api |
|||
VITE_APP_BASE_URL = 'http://pos-api.lingji.vip' |
|||
VITE_APP_BASE_PRE = 'http://pos-api.lingji.vip' |
|||
VITE_APP_BASE_NAME = 'POS' |
|||
|
|||
@ -0,0 +1,3 @@ |
|||
dist |
|||
node_modules |
|||
uni_modules |
|||
@ -0,0 +1,146 @@ |
|||
// @see https://eslint.bootcss.com/docs/rules/ |
|||
|
|||
module.exports = { |
|||
env: { |
|||
browser: true, |
|||
es2021: true, |
|||
node: true |
|||
}, |
|||
globals: { |
|||
NodeJS: 'readonly' |
|||
}, |
|||
/* 指定如何解析语法 */ |
|||
parser: 'vue-eslint-parser', |
|||
/** 优先级低于 parse 的语法解析配置 */ |
|||
parserOptions: { |
|||
ecmaVersion: 'latest', |
|||
sourceType: 'module', |
|||
parser: '@typescript-eslint/parser', |
|||
jsxPragma: 'React', |
|||
ecmaFeatures: { |
|||
jsx: true |
|||
} |
|||
}, |
|||
/* 继承已有的规则 */ |
|||
extends: ['eslint:recommended', 'plugin:vue/vue3-essential', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], |
|||
plugins: ['vue', '@typescript-eslint'], |
|||
overrides: [ |
|||
{ |
|||
files: ['*.ts', '*.tsx', '*.vue'], |
|||
rules: { |
|||
'no-undef': 0 |
|||
} |
|||
} |
|||
], |
|||
/* |
|||
* 'off' 或 0 ==> 关闭规则 |
|||
* 'warn' 或 1 ==> 打开的规则作为警告(不影响代码执行) |
|||
* 'error' 或 2 ==> 规则作为一个错误(代码不能执行,界面报错) |
|||
*/ |
|||
rules: { |
|||
// typeScript (https://typescript-eslint.io/rules) |
|||
'@typescript-eslint/no-unused-vars': [2, { argsIgnorePattern: '^_' }], // 禁止定义未使用的变量 |
|||
'@typescript-eslint/prefer-ts-expect-error': 2, // 禁止使用 @ts-ignore |
|||
'@typescript-eslint/no-explicit-any': 0, // 禁止使用 any 类型 |
|||
'@typescript-eslint/no-non-null-assertion': 0, |
|||
'@typescript-eslint/no-namespace': 0, // 禁止使用自定义 TypeScript 模块和命名空间。 |
|||
'@typescript-eslint/semi': 0, |
|||
'no-prototype-builtins': 0, // 可以使用obj.hasOwnProperty() |
|||
'@typescript-eslint/no-var-requires': 0, // 不允许在import 中使用require |
|||
'@typescript-eslint/no-empty-function': 2, // 关闭空方法检查 |
|||
// eslint-plugin-vue (https://eslint.vuejs.org/rules/) |
|||
'vue/multi-word-component-names': 0, // 要求组件名称始终为 “-” 链接的单词 |
|||
'vue/script-setup-uses-vars': 2, // 防止<script setup>使用的变量<template>被标记为未使用 |
|||
'vue/no-mutating-props': 0, // 不允许组件 prop的改变 |
|||
'vue/no-v-html': 0, // 禁止使用 v-html |
|||
'vue/no-setup-props-destructure': 0, // 禁止 props 解构传递给 setup |
|||
'vue/no-v-model-argument': 0, // 不允许添加要在 v-model 自定义组件中使用的参数 |
|||
'vue/component-definition-name-casing': [2, 'PascalCase'], // 强制使用组件定义名称的特定大小写 PascalCase | kebab-case |
|||
'vue/attribute-hyphenation': [2, 'always', { ignore: [] }], // 对模板中的自定义组件强制实施属性命名样式 |
|||
'vue/no-dupe-keys': [2, { groups: [] }], // 不允许重复字段名称 |
|||
'vue/no-dupe-v-else-if': 2, // 不允许 / v-else-if 链中的 v-if 重复条件 |
|||
'vue/no-duplicate-attributes': 2, // 禁止属性重复 |
|||
'vue/no-ref-as-operand': 2, // 使用ref对象必须.value |
|||
'vue/first-attribute-linebreak': [ |
|||
2, |
|||
{ |
|||
singleline: 'ignore', |
|||
multiline: 'below' |
|||
} |
|||
], // 强制设置第一个属性的位置 |
|||
|
|||
'@typescript-eslint/no-this-alias': [ |
|||
'warn', |
|||
{ |
|||
allowDestructuring: false, // Disallow `const { props, state } = this`; true by default |
|||
allowedNames: ['_this'] // this的別名可以为_this |
|||
} |
|||
], |
|||
// eslint(https://eslint.bootcss.com/docs/rules/) |
|||
// 'object-curly-spacing': 0, // 在大括号内强制执行一致的间距 |
|||
'no-unexpected-multiline': 2, // 禁止空余的多行 |
|||
'no-await-in-loop': 2, // 该规则不允许在循环体中使用 await |
|||
'no-dupe-else-if': 2, // 禁止 if-else-if 链中的重复条件 |
|||
'no-const-assign': 2, // 禁止重新分配 const 变量 |
|||
'no-dupe-keys': 2, // 禁止对象字面量中的重复键 |
|||
'no-multiple-empty-lines': ['warn', { max: 1 }], // 不允许多个空行 |
|||
'no-unused-vars': 0, // 禁止未使用的变量 |
|||
'use-isnan': 2, // 检查 NaN 时需要调用 isNaN() |
|||
'valid-typeof': 2, // 强制将 typeof 表达式与有效字符串进行比较 |
|||
'no-var': 2, // 要求使用 let 或 const 而不是 var |
|||
'no-extra-semi': 2, // 禁止不必要的分号 |
|||
'no-multi-str': 2, // 禁止多行字符串 |
|||
'no-unused-labels': 2, // 禁止未使用的标签 |
|||
'array-bracket-newline': [2, 'consistent'], // 在打开数组括号之后和关闭数组括号之前强制换行 |
|||
eqeqeq: [2, 'smart'], // 必须使用全等 |
|||
'arrow-spacing': 2, // 在箭头函数中的箭头前后强制执行一致的间距 |
|||
'function-call-argument-newline': [2, 'consistent'], // 在函数调用的参数之间强制换行 |
|||
'no-undef': 2, // 禁止使用未声明的变量,除非在 /*global */ 注释中提及 |
|||
complexity: [2, 15], |
|||
indent: [2, 4, { SwitchCase: 1 }], |
|||
'valid-jsdoc': 0, //jsdoc规则 |
|||
'no-console': 0, |
|||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, |
|||
'no-useless-escape': 0, // 禁止不必要的转义字符 |
|||
'@typescript-eslint/ban-types': 0, // 允许使用function 声明函数 |
|||
'prettier/prettier': [ |
|||
2, |
|||
{ |
|||
//在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x |
|||
arrowParens: 'always', |
|||
// 开始标签的右尖括号是否跟随在最后一行属性末尾,默认false |
|||
bracketSameLine: false, |
|||
// 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar}) |
|||
bracketSpacing: true, |
|||
// 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto) |
|||
embeddedLanguageFormatting: 'auto', |
|||
// 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css) |
|||
htmlWhitespaceSensitivity: 'ignore', |
|||
// 一行最多多少个字符 |
|||
printWidth: 150, |
|||
// 超出打印宽度 (always | never | preserve ) |
|||
proseWrap: 'preserve', |
|||
// 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;) |
|||
quoteProps: 'as-needed', |
|||
// 指定要使用的解析器,不需要写文件开头的 @prettier |
|||
requirePragma: false, |
|||
// 不需要自动在文件开头插入 @prettier |
|||
insertPragma: false, |
|||
// 最后不需要引号 |
|||
semi: false, |
|||
// 使用单引号 (true:单引号;false:双引号) |
|||
singleQuote: true, |
|||
// 缩进空格数,默认2个空格 |
|||
tabWidth: 4, |
|||
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none |
|||
trailingComma: 'none', |
|||
// 使用制表符而不是空格缩进行 |
|||
useTabs: false, |
|||
// Vue文件脚本和样式标签缩进 |
|||
vueIndentScriptAndStyle: false, |
|||
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>" |
|||
endOfLine: 'auto' |
|||
} |
|||
] |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
.DS_Store |
|||
/node_modules |
|||
/dist |
|||
|
|||
# local env files |
|||
.env.local |
|||
.env.*.local |
|||
|
|||
# Log files |
|||
npm-debug.log* |
|||
yarn-debug.log* |
|||
yarn-error.log* |
|||
|
|||
# Editor directories and files |
|||
.project |
|||
.idea |
|||
.vscode |
|||
*.suo |
|||
*.ntvs* |
|||
*.njsproj |
|||
*.sln |
|||
*.sw* |
|||
|
|||
/unpackage |
|||
.hbuilderx |
|||
|
|||
package-lock.josn |
|||
pnpm-lock.yaml |
|||
|
|||
/types/auto-imports.d.ts |
|||
@ -0,0 +1,4 @@ |
|||
#!/usr/bin/env sh |
|||
. "$(dirname -- "$0")/_/husky.sh" |
|||
|
|||
pnpm run fix |
|||
@ -0,0 +1,8 @@ |
|||
/dist/* |
|||
/html/* |
|||
.local |
|||
/node_modules/** |
|||
**/*.svg |
|||
**/*.sh |
|||
/public/* |
|||
/uni_modules/* |
|||
@ -0,0 +1,36 @@ |
|||
module.exports = { |
|||
//在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x |
|||
arrowParens: 'always', |
|||
// 开始标签的右尖括号是否跟随在最后一行属性末尾,默认false |
|||
bracketSameLine: false, |
|||
// 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar}) |
|||
bracketSpacing: true, |
|||
// 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto) |
|||
embeddedLanguageFormatting: 'auto', |
|||
// 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css) |
|||
htmlWhitespaceSensitivity: 'ignore', |
|||
// 一行最多多少个字符 |
|||
printWidth: 150, |
|||
// 超出打印宽度 (always | never | preserve ) |
|||
proseWrap: 'preserve', |
|||
// 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;) |
|||
quoteProps: 'as-needed', |
|||
// 指定要使用的解析器,不需要写文件开头的 @prettier |
|||
requirePragma: false, |
|||
// 不需要自动在文件开头插入 @prettier |
|||
insertPragma: false, |
|||
// 最后不需要引号 |
|||
semi: false, |
|||
// 使用单引号 (true:单引号;false:双引号) |
|||
singleQuote: true, |
|||
// 缩进空格数,默认2个空格 |
|||
tabWidth: 4, |
|||
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none |
|||
trailingComma: 'none', |
|||
// 使用制表符而不是空格缩进行 |
|||
useTabs: false, |
|||
// Vue文件脚本和样式标签缩进 |
|||
vueIndentScriptAndStyle: false, |
|||
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>" |
|||
endOfLine: 'auto' |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
const fs = require('fs') |
|||
|
|||
const pagesStr = fs.readFileSync('./src/pages.json', 'utf-8'); |
|||
const pagesJson = JSON.parse(pagesStr); |
|||
// Pages页面
|
|||
const Pages = pagesJson.pages.map((i) => { |
|||
return { |
|||
type: 'Pages', |
|||
name: i.name, |
|||
path: `/${i.path}`, |
|||
title: i.style?.navigationBarTitleText |
|||
}; |
|||
}); |
|||
// 二级页面
|
|||
const subPages = pagesJson.subPackages.flatMap((i) => { |
|||
return i.pages.map((x) => { |
|||
return { |
|||
type: 'subPage', |
|||
name: x.name, |
|||
path: `/${i.root}/${x.path}`, |
|||
title: x.style?.navigationBarTitleText |
|||
}; |
|||
}); |
|||
}); |
|||
// 当前已有页面
|
|||
// const pages = [...Pages, ...subPages];
|
|||
// 当前已创建文件
|
|||
const filesList = fs.readdirSync('./src/pages'); |
|||
const filesSubList = fs.readdirSync('./src/subpackage/pages'); |
|||
|
|||
// 获取需要新增的页面 =>取差集
|
|||
let newPages = Pages.filter((i) => !filesList.includes(i.name)); |
|||
const newSubPages = subPages.filter((i) => !filesSubList.includes(i.name)); |
|||
|
|||
newPages = [...newPages, ...newSubPages] |
|||
|
|||
// 添加新路由
|
|||
function addPages(pages) { |
|||
for (const page of pages) { |
|||
// 待修改根据path 路径生成
|
|||
const { name, title, type } = page; |
|||
let dirPath = '' |
|||
switch (type) { |
|||
case 'Pages': |
|||
// 主包
|
|||
dirPath = `./src/pages/${name}`; |
|||
break; |
|||
case 'subPage': |
|||
// 分包
|
|||
dirPath = `./src/subpackage/pages/${name}`; |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
// if (name.toLowerCase().indexOf("list") != -1) {
|
|||
// console.log(22222222);
|
|||
|
|||
// } else {
|
|||
// console.log(33333333);
|
|||
// }
|
|||
|
|||
// return
|
|||
fs.mkdirSync(dirPath); |
|||
const filePath = `${dirPath}/${name}.vue`; |
|||
const createStream = fs.createWriteStream(filePath); |
|||
|
|||
const template = |
|||
`<script setup lang="ts">
|
|||
import HeaderXcx from '@/components/Header/HeaderXcx.vue' |
|||
</script> |
|||
<template> |
|||
<view class=""> |
|||
<HeaderXcx :leftTxt="'标题'" :textColor="'#fff'" :goBack="false"></HeaderXcx> |
|||
${title} |
|||
</view> |
|||
</template> |
|||
<style lang="scss" scoped></style>`;
|
|||
createStream.write(template); |
|||
createStream.end(); |
|||
console.log('\x1B[34m', `pages ${name} created successfully.`); |
|||
} |
|||
console.log('\x1B[32m%s\x1B[39m', '\n All files are created successfully.\n'); |
|||
} |
|||
|
|||
addPages(newPages); |
|||
@ -0,0 +1,4 @@ |
|||
if (!/pnpm/.test(process.env.npm_execpath || '')) { |
|||
console.warn(`\u001b[33mThis repository must using pnpm as the package manager ` + ` for scripts to work properly.\u001b[39m\n`) |
|||
process.exit(1) |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8" /> |
|||
<script> |
|||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || |
|||
CSS.supports('top: constant(a)')) |
|||
document.write( |
|||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + |
|||
(coverSupport ? ', viewport-fit=cover' : '') + '" />') |
|||
</script> |
|||
<title></title> |
|||
<!--preload-links--> |
|||
<!--app-context--> |
|||
</head> |
|||
|
|||
<body> |
|||
<view id="app"><!--app-html--></view> |
|||
<script type="module" src="/src/main.ts"></script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,100 @@ |
|||
{ |
|||
"name": "uni-preset-vue", |
|||
"version": "0.0.0", |
|||
"scripts": { |
|||
"dev:app": "uni -p app", |
|||
"dev:app-android": "uni -p app-android", |
|||
"dev:app-ios": "uni -p app-ios", |
|||
"dev:custom": "uni -p", |
|||
"dev:h5": "uni", |
|||
"dev:h5:prop": "uni --mode=production", |
|||
"dev:h5:ssr": "uni --ssr", |
|||
"dev:mp-alipay": "uni -p mp-alipay", |
|||
"dev:mp-baidu": "uni -p mp-baidu", |
|||
"dev:mp-jd": "uni -p mp-jd", |
|||
"dev:mp-kuaishou": "uni -p mp-kuaishou", |
|||
"dev:mp-lark": "uni -p mp-lark", |
|||
"dev:mp-qq": "uni -p mp-qq", |
|||
"dev:mp-toutiao": "uni -p mp-toutiao", |
|||
"dev:mp-weixin": "uni -p mp-weixin", |
|||
"dev:quickapp-webview": "uni -p quickapp-webview", |
|||
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei", |
|||
"dev:quickapp-webview-union": "uni -p quickapp-webview-union", |
|||
"build:app": "uni build -p app", |
|||
"build:app-android": "uni build -p app-android", |
|||
"build:app-ios": "uni build -p app-ios", |
|||
"build:custom": "uni build -p", |
|||
"build:h5": "uni build", |
|||
"build:h5:ssr": "uni build --ssr", |
|||
"build:mp-alipay": "uni build -p mp-alipay", |
|||
"build:mp-baidu": "uni build -p mp-baidu", |
|||
"build:mp-jd": "uni build -p mp-jd", |
|||
"build:mp-kuaishou": "uni build -p mp-kuaishou", |
|||
"build:mp-lark": "uni build -p mp-lark", |
|||
"build:mp-qq": "uni build -p mp-qq", |
|||
"build:mp-toutiao": "uni build -p mp-toutiao", |
|||
"build:mp-weixin": "uni build -p mp-weixin", |
|||
"build:quickapp-webview": "uni build -p quickapp-webview", |
|||
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei", |
|||
"build:quickapp-webview-union": "uni build -p quickapp-webview-union", |
|||
"type-check": "vue-tsc --noEmit", |
|||
"add": "node ./auto/addPage.ts", |
|||
"preinstall": "node ./auto/preinstall.js", |
|||
"lint": "eslint --ext .ts,.js,.vue ./src", |
|||
"fix": "eslint --fix --ext .ts,.js,.vue ./src", |
|||
"prepare": "husky install", |
|||
"rm": "rm -rf node_modules package-lock.json pnpm-lock.yaml && pnpm install" |
|||
}, |
|||
"dependencies": { |
|||
"@dcloudio/uni-app": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-app-plus": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-components": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-h5": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-mp-alipay": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-mp-baidu": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-mp-jd": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-mp-kuaishou": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-mp-lark": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-mp-qq": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-mp-toutiao": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-mp-weixin": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-mp-xhs": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-quickapp-webview": "3.0.0-alpha-3081220230802001", |
|||
"@qiun/ucharts": "2.5.0-20230101", |
|||
"animate.css": "^4.1.1", |
|||
"echarts": "^5.5.0", |
|||
"pinia": "2.0.36", |
|||
"sass": "^1.63.2", |
|||
"uview-plus": "^3.4.9", |
|||
"vue": "^3.2.45", |
|||
"vue-i18n": "^9.1.9" |
|||
}, |
|||
"devDependencies": { |
|||
"@babel/eslint-parser": "^7.22.9", |
|||
"@dcloudio/types": "^3.3.2", |
|||
"@dcloudio/uni-automator": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-cli-shared": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/uni-stacktracey": "3.0.0-alpha-3081220230802001", |
|||
"@dcloudio/vite-plugin-uni": "3.0.0-alpha-3081220230802001", |
|||
"@typescript-eslint/eslint-plugin": "^6.2.1", |
|||
"@typescript-eslint/parser": "^6.2.1", |
|||
"@vue/tsconfig": "^0.1.3", |
|||
"@vueuse/core": "^10.3.0", |
|||
"eslint": "^8.46.0", |
|||
"eslint-config-prettier": "^8.9.0", |
|||
"eslint-plugin-import": "^2.28.0", |
|||
"eslint-plugin-node": "^11.1.0", |
|||
"eslint-plugin-prettier": "^5.0.0", |
|||
"eslint-plugin-vue": "^9.16.1", |
|||
"feng-uniapp-exploit": "^1.0.2", |
|||
"husky": "^8.0.0", |
|||
"pinia-plugin-unistorage": "^0.0.17", |
|||
"prettier": "^3.0.0", |
|||
"sass-loader": "^10.4.1", |
|||
"typescript": "^4.9.4", |
|||
"unplugin-auto-import": "^0.16.6", |
|||
"unplugin-vue-components": "^0.25.1", |
|||
"vite": "4.0.3", |
|||
"vue-tsc": "^1.0.24" |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
<template> |
|||
<view id="app"> |
|||
<router-view></router-view> |
|||
</view> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
// import routingIntercept from '@/permission' |
|||
|
|||
onLaunch(() => { |
|||
// routingIntercept() |
|||
}) |
|||
onShow(() => { |
|||
console.log('App Show') |
|||
}) |
|||
onHide(() => { |
|||
console.log('App Hide') |
|||
}) |
|||
// 全局变量 |
|||
// provide('globalObj', <globalObjInt>{ |
|||
// // 公用跳转方法 |
|||
// goToPage |
|||
// }); |
|||
// // 引入静态资源 |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
/* 注意要写在第一行,同时给style标签加入lang="scss"属性 */ |
|||
@import 'uview-plus/index.scss'; |
|||
</style> |
|||
@ -0,0 +1,25 @@ |
|||
import { request } from '@/utils/http' |
|||
|
|||
export function getVoteList(data: pageType) { |
|||
return request.http({ |
|||
url: '/api/vote_list', |
|||
method: 'GET', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
export function getVoteDetail(data: { id: number }) { |
|||
return request.http({ |
|||
url: '/api/vote_result_detail', |
|||
method: 'GET', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
export function getVoteResult(data: pageType) { |
|||
return request.http({ |
|||
url: '/api/vote_result', |
|||
method: 'GET', |
|||
data |
|||
}) |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
import { request } from '@/utils/http' |
|||
|
|||
export function getOpenid(data: { code: string }) { |
|||
return request.http({ |
|||
url: '/api/openid', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
export function getMobile(data: { code: string; openid: string }) { |
|||
return request.http({ |
|||
url: '/api/mobile', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
export function getAdminPhone() { |
|||
return request.http({ |
|||
url: '/api/admin_mobile', |
|||
method: 'GET' |
|||
}) |
|||
} |
|||
//用户签到
|
|||
export function getSign(data: { meetId: string; openid: string }) { |
|||
return request.http({ |
|||
url: '/api/sign', |
|||
method: 'GET', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
//扫码时手机号获取用户信息
|
|||
export function getmemberMobileGet(data: { meetId: string; openid: string; mobile: string }) { |
|||
return request.http({ |
|||
url: '/api/member_mobile_get', |
|||
method: 'GET', |
|||
data |
|||
}) |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
import { request } from '@/utils/http' |
|||
|
|||
export interface listType { |
|||
name: string |
|||
age: number |
|||
sex: number |
|||
nation: string |
|||
mobile: string |
|||
position: string |
|||
work_unit: string |
|||
} |
|||
|
|||
export interface dateListtype extends listType { |
|||
id: number |
|||
} |
|||
|
|||
export function infoForOpenid(data: { openid: string; mobile: string ; meetId: string }) { |
|||
return request.http({ |
|||
url: '/api/member_mobile_get', |
|||
method: 'GET', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 手机号获取用户信息
|
|||
export function getMember_mobile(data: any) { |
|||
return request.http({ |
|||
url: '/api/member_mobile', |
|||
method: 'GET', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
export function list(data: { name: string, meetId: string }) { |
|||
return request.http({ |
|||
url: '/api/member_name', |
|||
method: 'GET', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
export function update(data: { [n: string]: string }, openid: string) { |
|||
return request.http({ |
|||
url: '/api/member_update', |
|||
data: Object.assign(data, { openid }) |
|||
}) |
|||
} |
|||
|
|||
export function bind(data: { id: number; openid: string }) { |
|||
return request.http({ |
|||
url: '/api/name_sub', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
export function add(data: listType) { |
|||
return request.http({ |
|||
url: '/api/member_add', |
|||
data |
|||
}) |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
import { request } from '@/utils/http' |
|||
|
|||
// 获取投票选举(正在进行)
|
|||
export function getVoteprogress(id: string) { |
|||
return request.http({ |
|||
url: '/api/vote_progress?meetId='+ id, |
|||
method: 'GET' |
|||
}) |
|||
} |
|||
|
|||
//用户投票
|
|||
export function voteMember(data: any) { |
|||
return request.http({ |
|||
url: '/api/vote_member', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 获取我的选举
|
|||
export function getMyvote(data: any) { |
|||
return request.http({ |
|||
url: '/api/my_vote', |
|||
method: 'GET', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 手机号获取用户信息
|
|||
export function getMember_mobile(data: any) { |
|||
return request.http({ |
|||
url: '/api/member_mobile', |
|||
method: 'GET', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 判断用户是否签到
|
|||
export function getIs_sign(data: any) { |
|||
return request.http({ |
|||
url: '/api/is_sign', |
|||
method: 'GET', |
|||
data |
|||
}) |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
export const Prefix = 'Election_' |
|||
|
|||
export const getPrefixName = (name: string) => { |
|||
return Prefix + name |
|||
} |
|||
|
|||
export function getAuthorization() { |
|||
return uni.getStorageSync(getPrefixName('user')) |
|||
} |
|||
|
|||
export function removeAuthorization() { |
|||
return uni.removeStorageSync(getPrefixName('user')) |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
import { createSSRApp } from 'vue' |
|||
import App from './App.vue' |
|||
|
|||
import uviewPlus from 'uview-plus' |
|||
import fengUniappExploit from 'feng-uniapp-exploit' |
|||
|
|||
import store from './store' |
|||
|
|||
export function createApp() { |
|||
const app = createSSRApp(App) |
|||
app.use(uviewPlus) |
|||
app.use(fengUniappExploit) |
|||
app.use(store) |
|||
|
|||
return { app } |
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
{ |
|||
"name" : "惠企通", |
|||
"appid" : "__UNI__4CC99EE", |
|||
"description" : "v3+ts+uniapp模版", |
|||
"versionName" : "1.0.68", |
|||
"versionCode" : 168, |
|||
"transformPx" : false, |
|||
/* 5+App特有相关 */ |
|||
"app-plus" : { |
|||
"usingComponents" : true, |
|||
"nvueStyleCompiler" : "uni-app", |
|||
"compilerVersion" : 3, |
|||
"splashscreen" : { |
|||
"alwaysShowBeforeRender" : true, |
|||
"waiting" : true, |
|||
"autoclose" : true, |
|||
"delay" : 0 |
|||
}, |
|||
"compatible" : { |
|||
"ignoreVersion" : true |
|||
}, |
|||
/* 模块配置 */ |
|||
"modules" : { |
|||
"Barcode" : {}, |
|||
"Camera" : {}, |
|||
"VideoPlayer" : {}, |
|||
"Share" : {}, |
|||
"Geolocation" : {}, |
|||
"Maps" : {} |
|||
}, |
|||
/* 应用发布信息 */ |
|||
"distribute" : { |
|||
/* android打包配置 */ |
|||
"android" : { |
|||
"permissions" : [ |
|||
"<uses-feature android:name=\"android.hardware.camera\"/>", |
|||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", |
|||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>", |
|||
"<uses-permission android:name=\"android.permission.CAMERA\"/>", |
|||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", |
|||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", |
|||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>", |
|||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>", |
|||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", |
|||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" |
|||
], |
|||
"minSdkVersion" : 21 |
|||
}, |
|||
/* ios打包配置 */ |
|||
"ios" : { |
|||
"dSYMs" : false |
|||
}, |
|||
/* SDK配置 */ |
|||
"sdkConfigs" : { |
|||
"ad" : {}, |
|||
"share" : { |
|||
"weixin" : { |
|||
"appid" : "wx5d1a07b75bd48225", |
|||
"UniversalLinks" : "" |
|||
} |
|||
}, |
|||
"geolocation" : { |
|||
"amap" : { |
|||
"name" : "amap_18648278829CcsBXVUm1", |
|||
"__platform__" : [ "ios", "android" ], |
|||
"appkey_ios" : "37180416cb95db05dc9639e616655a7a", |
|||
"appkey_android" : "37180416cb95db05dc9639e616655a7a" |
|||
} |
|||
}, |
|||
"maps" : { |
|||
"amap" : { |
|||
"name" : "amap_18648278829CcsBXVUm1", |
|||
"appkey_ios" : "37180416cb95db05dc9639e616655a7a", |
|||
"appkey_android" : "37180416cb95db05dc9639e616655a7a" |
|||
} |
|||
} |
|||
}, |
|||
"icons" : { |
|||
"android" : { |
|||
"hdpi" : "src/static/logo.png", |
|||
"xhdpi" : "src/static/logo.png", |
|||
"xxhdpi" : "src/static/logo.png", |
|||
"xxxhdpi" : "src/static/logo.png" |
|||
} |
|||
}, |
|||
"splashscreen" : { |
|||
"iosStyle" : "common", |
|||
"androidStyle" : "default", |
|||
"android" : { |
|||
"hdpi" : "", |
|||
"xhdpi" : "", |
|||
"xxhdpi" : "" |
|||
}, |
|||
"ios" : { |
|||
"storyboard" : "C:/Users/Lenovo/Desktop/CustomStoryboard.zip" |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
/* 快应用特有相关 */ |
|||
"quickapp" : {}, |
|||
/* 小程序特有相关 */ |
|||
"mp-weixin" : { |
|||
"appid" : "wx4f9dae5cc37dd9f4", |
|||
"setting" : { |
|||
"urlCheck" : false, |
|||
"es6" : true, |
|||
"postcss" : false, |
|||
"minified" : true |
|||
}, |
|||
"usingComponents" : true |
|||
}, |
|||
"mp-alipay" : { |
|||
"usingComponents" : true |
|||
}, |
|||
"mp-baidu" : { |
|||
"usingComponents" : true |
|||
}, |
|||
"mp-toutiao" : { |
|||
"usingComponents" : true |
|||
}, |
|||
"uniStatistics" : { |
|||
"enable" : false |
|||
}, |
|||
"vueVersion" : "3" |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
{ |
|||
"easycom": { |
|||
"autoscan": true, |
|||
"custom": { |
|||
"^ex-(.*)": "feng-uniapp-exploit/components/ex-$1/ex-$1.vue", |
|||
"^u-(.*)": "uview-plus/components/u-$1/u-$1.vue" |
|||
} |
|||
}, |
|||
"pages": [ |
|||
{ |
|||
"path": "pages/index/index", |
|||
"style": { |
|||
"navigationBarTitleText": "首页", |
|||
"enablePullDownRefresh": true |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/memberCenter/index", |
|||
"style": { |
|||
"navigationBarTitleText": "会员中心", |
|||
"enablePullDownRefresh": true |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/learningCenter/index", |
|||
"style": { |
|||
"navigationBarTitleText": "学习中心", |
|||
"enablePullDownRefresh": true |
|||
} |
|||
}, |
|||
{ |
|||
"name": "user", |
|||
"path": "pages/mine/index", |
|||
"style": { |
|||
"navigationBarTitleText": "个人中心", |
|||
"enablePullDownRefresh": true |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/login/login", |
|||
"style": { |
|||
"navigationBarTitleText": "登录", |
|||
"enablePullDownRefresh": false, |
|||
"navigationStyle": "custom" |
|||
} |
|||
} |
|||
], |
|||
"globalStyle": { |
|||
"navigationBarTextStyle": "white", |
|||
"navigationBarBackgroundColor": "#F1F3F9", |
|||
"backgroundColor": "#F8F8F8" |
|||
}, |
|||
"tabBar": { |
|||
"color": "#9CA3AF", |
|||
"selectedColor": "#2563EB", |
|||
"borderStyle": "white", |
|||
"backgroundColor": "#FFFFFF", |
|||
"list": [ |
|||
{ |
|||
"pagePath": "pages/index/index", |
|||
"iconPath": "static/tabbar/index.png", |
|||
"selectedIconPath": "static/tabbar/index_select.png", |
|||
"text": "首页" |
|||
}, |
|||
{ |
|||
"pagePath": "pages/memberCenter/index", |
|||
"iconPath": "static/tabbar/hyzx.png", |
|||
"selectedIconPath": "static/tabbar/hyzx_select.png", |
|||
"text": "会员中心" |
|||
}, |
|||
{ |
|||
"pagePath": "pages/learningCenter/index", |
|||
"iconPath": "static/tabbar/xxzx.png", |
|||
"selectedIconPath": "static/tabbar/xxzx_select.png", |
|||
"text": "学习中心" |
|||
}, |
|||
{ |
|||
"pagePath": "pages/mine/index", |
|||
"iconPath": "static/tabbar/grzx.png", |
|||
"selectedIconPath": "static/tabbar/grzx_select.png", |
|||
"text": "个人中心" |
|||
} |
|||
] |
|||
} |
|||
} |
|||
@ -0,0 +1,283 @@ |
|||
<template> |
|||
<view class="container"> |
|||
<scroll-view v-if="electionList.length !== 0" class="scroll-view" scroll-y @scrolltolower="loadMore" :show-scrollbar="false"> |
|||
<view v-for="(item, index) in electionList" :key="index" class="election-item"> |
|||
<view class="year-title"> |
|||
<view class="headpart"> |
|||
<text class="title">{{ item.vote_title }}</text> |
|||
<view |
|||
class="type" |
|||
:style=" |
|||
item.vote_title == 1 |
|||
? 'background: #DBEAFE;color: #3B82F6;' |
|||
: item.vote_title == 2 |
|||
? 'background: #DCFCE7;color: #10B981' |
|||
: 'background: #F3F4F6;color: #4B5563' |
|||
" |
|||
> |
|||
{{ item.status == 1 ? '未开始' : item.status == 2 ? '进行中' : '已结束' }} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view class="flex-center-between" style="display: flex" v-show="expandedStates[index]"> |
|||
<view style="display: grid"> |
|||
<view |
|||
v-for="(candidate, cIndex) in item.candidate.slice(0, expandedStates[index] ? cIndex : 1)" |
|||
:key="cIndex" |
|||
class="candidate-item" |
|||
> |
|||
<view class="info-section"> |
|||
<view style="display: flex; align-items: center"> |
|||
<img style="width: 96rpx; height: 96rpx; border-radius: 50%" :src="candidate.photo" alt="" /> |
|||
<view style="margin-left: 24rpx; display: grid"> |
|||
<text class="name">{{ candidate.name }}</text> |
|||
<text class="college">{{ candidate.position }}</text> |
|||
</view> |
|||
</view> |
|||
<view style="display: flex; margin-top: 24rpx; font-family: Roboto; font-size: 28rpx; color: #4b5563"> |
|||
我的选择: |
|||
<view :class="['choice-tag', choiceClass(candidate.vote_result)]"> |
|||
{{ choiceText(candidate.vote_result) }} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view class="expand-btn" @click="toggleExpand(index)"> |
|||
<text :class="['arrow', expandedStates[index] ? 'up' : 'down']"></text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</scroll-view> |
|||
<view v-else> |
|||
<ex-empty :height="55" :tips="'暂无数据~'" /> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref, watch } from 'vue' |
|||
import { getMyvote } from '../../api/votingElection' |
|||
import useUserStore from '@/store/user' |
|||
const userStore = useUserStore() |
|||
|
|||
const electionList = ref([]) |
|||
const page = ref(1) |
|||
const pageSize = ref(10) |
|||
const loading = ref(false) |
|||
const noMoreData = ref(false) |
|||
|
|||
// 展开状态管理 |
|||
const expandedStates = ref([]) |
|||
watch( |
|||
() => electionList.value, |
|||
(newVal) => { |
|||
expandedStates.value = newVal.map(() => false) |
|||
}, |
|||
{ |
|||
immediate: true |
|||
} |
|||
) |
|||
|
|||
const toggleExpand = (index) => { |
|||
expandedStates.value[index] = !expandedStates.value[index] |
|||
} |
|||
|
|||
const choiceClass = (choice) => { |
|||
switch (choice) { |
|||
case 1: |
|||
return 'agree' |
|||
case 2: |
|||
return 'oppose' |
|||
case 3: |
|||
return 'abstain' |
|||
default: |
|||
return '' |
|||
} |
|||
} |
|||
|
|||
const choiceText = (choice) => { |
|||
return ( |
|||
{ |
|||
1: '同意', |
|||
2: '反对', |
|||
3: '弃权' |
|||
}[choice] || '' |
|||
) |
|||
} |
|||
|
|||
// 获取数据 |
|||
const getList = async () => { |
|||
try { |
|||
loading.value = true |
|||
|
|||
let param = { |
|||
openid: userStore.openId, |
|||
page: page.value, |
|||
limit: pageSize.value |
|||
} |
|||
// 模拟接口请求(替换为你的真实接口) |
|||
const mockData = await getMyvote(param) |
|||
|
|||
// 处理数据 |
|||
electionList.value = [...electionList.value, ...mockData.data.data] |
|||
// electionList.value.push(mockData) |
|||
|
|||
// 判断是否还有数据 |
|||
noMoreData.value = mockData.data.data.length < pageSize.value |
|||
} finally { |
|||
loading.value = false |
|||
} |
|||
} |
|||
|
|||
// 加载更多 |
|||
const loadMore = () => { |
|||
if (loading.value || noMoreData.value) return |
|||
page.value += 1 |
|||
getList() |
|||
} |
|||
|
|||
onShow(() => { |
|||
electionList.value = [] |
|||
getList() |
|||
}) |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
.container { |
|||
padding: 20rpx; |
|||
background-color: #f9fafb; |
|||
height: 100vh; |
|||
} |
|||
|
|||
.election-item { |
|||
margin-bottom: 30rpx; |
|||
padding: 34rpx; |
|||
border-radius: 24rpx; |
|||
background: linear-gradient(0deg, rgba(0, 0, 0, 0.001), rgba(0, 0, 0, 0.001)), #ffffff; |
|||
box-sizing: border-box; |
|||
border: 2rpx solid #f3f4f6; |
|||
box-shadow: 0rpx 2rpx 4rpx 0rpx rgba(0, 0, 0, 0.05); |
|||
} |
|||
|
|||
.year-title { |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
|
|||
.headpart { |
|||
width: 100%; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
|
|||
.title { |
|||
font-family: Roboto; |
|||
font-size: 32rpx; |
|||
font-weight: 800; |
|||
letter-spacing: normal; |
|||
color: #000000; |
|||
} |
|||
|
|||
.type { |
|||
font-family: Roboto; |
|||
font-size: 24rpx; |
|||
font-weight: normal; |
|||
/* 自动布局 */ |
|||
display: flex; |
|||
flex-direction: column; |
|||
padding: 4rpx 16rpx; |
|||
gap: 0rpx 20rpx; |
|||
flex-wrap: wrap; |
|||
align-content: flex-start; |
|||
border-radius: 24rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.candidate-item { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 20rpx 0; |
|||
} |
|||
|
|||
.info-section { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.name { |
|||
font-family: Roboto; |
|||
font-size: 28rpx; |
|||
font-weight: 500; |
|||
letter-spacing: normal; |
|||
color: #000000; |
|||
} |
|||
|
|||
.college { |
|||
font-family: Roboto; |
|||
font-size: 24rpx; |
|||
font-weight: normal; |
|||
letter-spacing: normal; |
|||
color: #6b7280; |
|||
} |
|||
|
|||
.choice-tag { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
font-family: Roboto; |
|||
font-size: 28rpx; |
|||
font-weight: normal; |
|||
} |
|||
|
|||
.agree { |
|||
color: #3b82f6; |
|||
} |
|||
|
|||
.oppose { |
|||
color: #f44336; |
|||
} |
|||
|
|||
.abstain { |
|||
color: #9ca3af; |
|||
} |
|||
|
|||
.year-title { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
.expand-btn { |
|||
display: flex; |
|||
align-items: center; |
|||
color: #666; |
|||
font-size: 26rpx; |
|||
padding: 10rpx 20rpx; |
|||
} |
|||
|
|||
.arrow { |
|||
display: inline-block; |
|||
width: 0; |
|||
height: 0; |
|||
margin-left: 10rpx; |
|||
border-left: 10rpx solid transparent; |
|||
border-right: 10rpx solid transparent; |
|||
} |
|||
|
|||
.down { |
|||
border-top: 15rpx solid #999; |
|||
} |
|||
|
|||
.up { |
|||
border-bottom: 15rpx solid #999; |
|||
} |
|||
|
|||
/* 优化候选人项间距 */ |
|||
.candidate-item:last-child { |
|||
border-bottom: none; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,27 @@ |
|||
<template> |
|||
<view class="container"> |
|||
|
|||
</view> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref, watch } from 'vue' |
|||
|
|||
|
|||
|
|||
|
|||
onShow(() => { |
|||
electionList.value = [] |
|||
getList() |
|||
}) |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
.container { |
|||
padding: 20rpx; |
|||
background-color: #f9fafb; |
|||
height: 100vh; |
|||
} |
|||
|
|||
|
|||
</style> |
|||
@ -0,0 +1,111 @@ |
|||
<script setup lang="ts"> |
|||
import { debounce } from 'feng-uniapp-exploit/utils/index' |
|||
import { getMobile, getSign} from '@/api/login' |
|||
|
|||
import useUserStore from '@/store/user' |
|||
const userStore = useUserStore() |
|||
|
|||
const loginCode = ref('') |
|||
|
|||
// 获取微信登录 code |
|||
const getLoginCode = async () : Promise<string> => { |
|||
try { |
|||
const res = await uni.login({ provider: 'weixin' }) |
|||
loginCode.value = res.code |
|||
return res.code |
|||
} catch (error) { |
|||
console.error('获取登录code失败:', error) |
|||
throw error |
|||
} |
|||
} |
|||
|
|||
// 处理获取手机号 |
|||
|
|||
interface phoneEvent { |
|||
detail : { errMsg : string; iv : string; encryptedData : string; code : string } |
|||
} |
|||
const onGetPhoneNumber = debounce(async (e : phoneEvent) => { |
|||
if (e.detail.errMsg.includes('fail')) { |
|||
uni.showToast({ title: '用户拒绝授权', icon: 'none' }) |
|||
return |
|||
} |
|||
|
|||
try { |
|||
if (!loginCode.value) { |
|||
await getLoginCode() |
|||
} |
|||
|
|||
if (!userStore.openId) { |
|||
await userStore.getopenid({ code: loginCode.value }) |
|||
} |
|||
|
|||
getMobile({ openid: userStore.openId, code: e.detail.code }) |
|||
.then((res) => { |
|||
const { data: phone } = res as { data : string } |
|||
|
|||
userStore.mobile = phone |
|||
|
|||
userStore.getUserInfo() |
|||
|
|||
onLoginSuccess() |
|||
}) |
|||
.catch(() => { |
|||
uni.showToast({ title: '登录失败!', icon: 'none' }) |
|||
}) |
|||
} catch (error) { |
|||
uni.showToast({ title: '登录失败', icon: 'none' }) |
|||
} |
|||
}) |
|||
|
|||
const onLoginSuccess = () => { |
|||
uni.showToast({ |
|||
title: '登录成功', |
|||
icon: 'none', |
|||
success: () => { |
|||
setTimeout(() => { |
|||
userStore.showtoast = false |
|||
|
|||
uni.reLaunch({ url: '/pages/votingElection/index?meetId=' + userStore.meetId }) |
|||
}, 1000) |
|||
} |
|||
}) |
|||
|
|||
} |
|||
onLoad((options : any) => { |
|||
|
|||
}) |
|||
|
|||
onShow(() => { |
|||
// if (userStore.mobile) { |
|||
// onLoginSuccess() |
|||
// } |
|||
}) |
|||
</script> |
|||
<template> |
|||
<view class="login"> |
|||
<image class="logo-img" src="@/static/logo.png" mode="widthFix" /> |
|||
|
|||
<view class="btn_box"> |
|||
<u-button @getphonenumber="onGetPhoneNumber" text="请签到后进行投票" icon-color="#fff" open-type="getPhoneNumber" |
|||
color="linear-gradient(270deg, rgba(232, 123, 7, 1) 0%, rgba(247, 205, 77, 1) 100%)" shape="circle" /> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.login { |
|||
width: 100%; |
|||
height: 100vh; |
|||
overflow: hidden; |
|||
position: relative; |
|||
text-align: center; |
|||
box-sizing: border-box; |
|||
padding: 500rpx 30rpx 0; |
|||
background-color: #fff; |
|||
|
|||
.logo-img { |
|||
width: 220rpx; |
|||
margin-bottom: 60rpx; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,203 @@ |
|||
<script setup lang="ts"> |
|||
import { getVoteResult } from '@/api/common' |
|||
const navto = (url: string, params = {}) => uni.$util.goToPage({ url, params }) |
|||
const doSearch = (formData: { page: number; limit: number }, onSuccess: Function) => { |
|||
getVoteResult(formData).then((res) => { |
|||
const { data } = res as { data: { data: any; total: number } } |
|||
onSuccess({ data }) |
|||
}) |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<view class="electionList"> |
|||
<ex-list ref="reListRef" custom-list-type="scroll" :on-form-search="doSearch"> |
|||
<template v-slot="{ row, index }"> |
|||
<view class="items" :uid="'items' + index"> |
|||
<view class="flex"> |
|||
<view class="flex1"> |
|||
<view class="flex-center-start"> |
|||
<text class="text-ellipsis title">{{ row.title }}</text> |
|||
<text class="status b" v-if="row.status === 2">进行中</text> |
|||
<text class="status f" v-else-if="row.status === 1">未开始</text> |
|||
<text class="status e" v-else>已结束</text> |
|||
</view> |
|||
<view class="time">{{ `投票时间:${row.start_time} 至 ${row.end_time}` }}</view> |
|||
</view> |
|||
<view :class="{ arrow: true, active: row.showInfo }" @click="row.showInfo = !row.showInfo"> |
|||
<u-icon name="arrow-down" color="#9CA3AF" /> |
|||
</view> |
|||
</view> |
|||
<view class="info" v-if="row.showInfo"> |
|||
<view class="flex info-items" v-for="(v, k) of row.candidate" :key="k"> |
|||
<view class="head"> |
|||
<image :src="v.photo" mode="aspectFill" /> |
|||
</view> |
|||
<view class="content flex1"> |
|||
<view class="name flex-center-start"> |
|||
<text>{{ v.name }}</text> |
|||
<text class="status" v-if="v.vote_result == 1">当选</text> |
|||
<text class="status un" v-else>未当选</text> |
|||
</view> |
|||
<view class="votes">得票数:{{ v.agree_num }}</view> |
|||
</view> |
|||
|
|||
<view class="progress"> |
|||
<u-line-progress :percentage="v.agree_percent" height="8rpx" active-color="#2563EB" :show-text="false" /> |
|||
<view class="progress-text">{{ v.agree_percent }}%</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="info-bts" v-if="row.status === 3" @click.stop="navto('pages/electionList/info', { id: row.id })"> |
|||
查看投票详情 |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
</ex-list> |
|||
</view> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.electionList { |
|||
width: 100%; |
|||
min-height: 100vh; |
|||
box-sizing: border-box; |
|||
padding: 32rpx; |
|||
background-color: #f9fafb; |
|||
|
|||
.items { |
|||
padding: 32rpx; |
|||
border-radius: 12px; |
|||
background: linear-gradient(0deg, rgba(0, 0, 0, 0.001), rgba(0, 0, 0, 0.001)), #ffffff; |
|||
box-sizing: border-box; |
|||
border: 1px solid #f3f4f6; |
|||
box-shadow: |
|||
0px 1px 2px -1px rgba(0, 0, 0, 0.1), |
|||
0px 1px 3px 0px rgba(0, 0, 0, 0.1); |
|||
margin-bottom: 32rpx; |
|||
|
|||
.text-ellipsis { |
|||
overflow: hidden; |
|||
white-space: noraml; |
|||
text-overflow: ellipsis; |
|||
} |
|||
|
|||
.title { |
|||
color: #000; |
|||
font-size: 32rpx; |
|||
font-weight: 500; |
|||
line-height: 48rpx; |
|||
} |
|||
|
|||
.status { |
|||
font-size: 24rpx; |
|||
border-radius: 20rpx; |
|||
padding: 0 16rpx; |
|||
margin-left: 16rpx; |
|||
white-space: nowrap; |
|||
|
|||
&.b { |
|||
color: #fff; |
|||
background-color: #2563eb; |
|||
} |
|||
|
|||
&.f { |
|||
color: #6b7280; |
|||
background-color: #e5e7eb; |
|||
} |
|||
|
|||
&.e { |
|||
color: #fff; |
|||
background-color: #ef4444; |
|||
} |
|||
} |
|||
|
|||
.time { |
|||
padding-top: 8rpx; |
|||
color: #6b7280; |
|||
line-height: 40rpx; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.arrow { |
|||
transition: 0.3s all ease-in; |
|||
|
|||
&.active { |
|||
transform: rotateZ(180deg); |
|||
} |
|||
} |
|||
|
|||
.info { |
|||
&-items { |
|||
margin-top: 32rpx; |
|||
|
|||
.head { |
|||
width: 96rpx; |
|||
height: 96rpx; |
|||
border-radius: 50%; |
|||
overflow: hidden; |
|||
background-color: #6b7280; |
|||
margin-right: 24rpx; |
|||
} |
|||
|
|||
.content { |
|||
height: min-content; |
|||
|
|||
.name { |
|||
font-size: 28rpx; |
|||
font-weight: 500; |
|||
line-height: 40rpx; |
|||
color: #000000; |
|||
|
|||
.status { |
|||
font-size: 20rpx; |
|||
font-weight: normal; |
|||
border-radius: 999rpx; |
|||
padding: 8rpx 24rpx; |
|||
line-height: 20rpx; |
|||
color: #2563eb; |
|||
background: rgba(37, 99, 235, 0.1); |
|||
|
|||
&.un { |
|||
color: #f44336; |
|||
background: rgba(244, 67, 54, 0.1); |
|||
} |
|||
} |
|||
} |
|||
|
|||
.votes { |
|||
font-size: 28rpx; |
|||
line-height: 40rpx; |
|||
color: #6b7280; |
|||
} |
|||
} |
|||
|
|||
.progress { |
|||
width: 192rpx; |
|||
height: min-content; |
|||
|
|||
&-text { |
|||
padding-top: 8rpx; |
|||
color: #6b7280; |
|||
font-size: 24rpx; |
|||
line-height: 32rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
&-bts { |
|||
padding: 16rpx; |
|||
margin: 48rpx 0 24rpx; |
|||
font-size: 28rpx; |
|||
font-weight: normal; |
|||
line-height: 42rpx; |
|||
text-align: center; |
|||
color: #ffffff; |
|||
background-color: #2563eb; |
|||
border-radius: 8rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,331 @@ |
|||
<script setup lang="ts"> |
|||
import useUserStore from '@/store/user' |
|||
import { list as listApi, type dateListtype, update as updateApi, getMember_mobile} from '@/api/user' |
|||
import { getAdminPhone } from '@/api/login' |
|||
|
|||
const userStore = useUserStore() |
|||
|
|||
const isHandleUserInfo = computed(() => Object.keys(userStore.userInfo).length === 0) |
|||
|
|||
const keyword = ref('') |
|||
|
|||
const list = ref<dateListtype[]>([]) |
|||
|
|||
const activeKey = ref(0) |
|||
|
|||
const amdinPhone = ref('') |
|||
|
|||
const navto = (url: string, params = {}) => uni.$util.goToPage({ url, params }) |
|||
|
|||
|
|||
|
|||
const getList = uni.$util.throttle(() => { |
|||
if (keyword.value === '') { |
|||
uni.showToast({ title: '请输入姓名', icon: 'none' }) |
|||
return |
|||
} |
|||
|
|||
// uni.showLoading({ mask: true, title: '加载中...' }) |
|||
|
|||
listApi({ name: keyword.value, meetId: userStore.meetId }) |
|||
.then((res) => { |
|||
const { data } = res as { data: dateListtype[] } |
|||
list.value = data || [] |
|||
// uni.hideLoading() |
|||
}) |
|||
.catch(() => uni.hideLoading()) |
|||
}) |
|||
|
|||
const updateUserInfo = () => { |
|||
userStore.bindUser(list.value[activeKey.value].id as number).then(() => { |
|||
reset() |
|||
uni.showToast({ title: '提交成功,等待投票!', icon: 'none' }) |
|||
userStore.getUserInfo() |
|||
|
|||
}) |
|||
} |
|||
|
|||
const reset = () => { |
|||
activeKey.value = 0 |
|||
keyword.value = '' |
|||
list.value = [] |
|||
} |
|||
|
|||
const showEdit = ref(false) |
|||
|
|||
const popFrom = reactive({ |
|||
label: '', |
|||
key: '', |
|||
value: '' |
|||
}) |
|||
const openPopup = (key: string, label: string) => { |
|||
popFrom.label = label |
|||
popFrom.key = key |
|||
popFrom.value = userStore.userInfo[key] |
|||
showEdit.value = true |
|||
} |
|||
|
|||
const save = () => { |
|||
if (popFrom.value === '') { |
|||
uni.showToast({ title: `请输入${popFrom.label}`, icon: 'none' }) |
|||
return |
|||
} |
|||
|
|||
uni.showLoading({ mask: true, title: '保存中...' }) |
|||
|
|||
updateApi({ [popFrom.key]: popFrom.value }, userStore.openId).then(() => { |
|||
uni.hideLoading() |
|||
userStore.userInfo[popFrom.key] = popFrom.value |
|||
showEdit.value = false |
|||
}) |
|||
} |
|||
|
|||
|
|||
onShow(() => { |
|||
reset() |
|||
getAdminPhone().then((res) => { |
|||
const { data } = res as { data: string } |
|||
amdinPhone.value = data || '' |
|||
|
|||
}) |
|||
}) |
|||
|
|||
onPullDownRefresh(() => userStore.getUserInfo()) |
|||
</script> |
|||
<template> |
|||
<view class="userview"> |
|||
<!-- 搜索信息 --> |
|||
<block v-if="isHandleUserInfo"> |
|||
<view class="userview-search box flex"> |
|||
<view class="userview-search-label">姓名</view> |
|||
<input class="flex1" type="text" placeholder="请输入您的姓名" v-model="keyword" placeholder-class="placeholder" /> |
|||
<view class="userview-search-bts" @click.stop="getList">搜索</view> |
|||
</view> |
|||
|
|||
<block v-if="list.length > 0"> |
|||
<view |
|||
class="userview-info box" |
|||
:class="{ active: activeKey === index }" |
|||
v-for="(row, index) in list" |
|||
:key="index" |
|||
@click="activeKey = index" |
|||
> |
|||
<view class="flex-center-between userview-info-item"> |
|||
<view class="userview-info-item-label">姓名</view> |
|||
<view class="userview-info-item-content flex">{{ row.name }}</view> |
|||
</view> |
|||
<view class="flex-center-between userview-info-item"> |
|||
<view class="userview-info-item-label">电话</view> |
|||
<view class="userview-info-item-content flex">{{ row.mobile }}</view> |
|||
</view> |
|||
<view class="flex-center-between userview-info-item"> |
|||
<view class="userview-info-item-label">工作单位</view> |
|||
<view class="userview-info-item-content flex">{{ row.work_unit }}</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
<ex-empty v-show="list.length <= 0" /> |
|||
<view class="flex-center-evenly"> |
|||
<view class="bts plain" v-if="list.length > 0" @click.stop="updateUserInfo">确认信息</view> |
|||
<view class="bts" @click="navto('pages/mine/add')">新建信息</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<!-- 详细信息 --> |
|||
<block v-else> |
|||
<view class="userview-info box"> |
|||
<view class="flex-center-between userview-info-item"> |
|||
<view class="userview-info-item-label">姓名</view> |
|||
<view class="userview-info-item-content flex"> |
|||
<text>{{ userStore.userInfo.name }}</text> |
|||
</view> |
|||
</view> |
|||
<view class="flex-center-between userview-info-item"> |
|||
<view class="userview-info-item-label">电话</view> |
|||
<view class="userview-info-item-content flex"> |
|||
<text>{{ userStore.userInfo.mobile }}</text> |
|||
</view> |
|||
</view> |
|||
<view class="flex-center-between userview-info-item"> |
|||
<view class="userview-info-item-label">性别</view> |
|||
<view class="userview-info-item-content flex"> |
|||
<text>{{ userStore.userInfo.sex === 1 ? '男' : '女' }}</text> |
|||
<u-icon @click.stop="openPopup('sex', '性别')" name="edit-pen-fill" color="#2563EB" size="38rpx" /> |
|||
</view> |
|||
</view> |
|||
<view class="flex-center-between userview-info-item"> |
|||
<view class="userview-info-item-label">民族</view> |
|||
<view class="userview-info-item-content flex"> |
|||
<text>{{ userStore.userInfo.nation }}</text> |
|||
<u-icon @click.stop="openPopup('nation', '民族')" name="edit-pen-fill" color="#2563EB" size="38rpx" /> |
|||
</view> |
|||
</view> |
|||
<view class="flex-center-between userview-info-item"> |
|||
<view class="userview-info-item-label">年龄</view> |
|||
<view class="userview-info-item-content flex"> |
|||
<text>{{ userStore.userInfo.age }}</text> |
|||
<u-icon @click.stop="openPopup('age', '年龄')" name="edit-pen-fill" color="#2563EB" size="38rpx" /> |
|||
</view> |
|||
</view> |
|||
<view class="flex-center-between userview-info-item"> |
|||
<view class="userview-info-item-label">工作单位</view> |
|||
<view class="userview-info-item-content flex"> |
|||
<text>{{ userStore.userInfo.work_unit }}</text> |
|||
<u-icon @click.stop="openPopup('work_unit', '工作单位')" name="edit-pen-fill" color="#2563EB" size="38rpx" /> |
|||
</view> |
|||
</view> |
|||
<view class="flex-center-between userview-info-item"> |
|||
<view class="userview-info-item-label">职位</view> |
|||
<view class="userview-info-item-content flex"> |
|||
<text>{{ userStore.userInfo.position }}</text> |
|||
<u-icon @click.stop="openPopup('position', '职位')" name="edit-pen-fill" color="#2563EB" size="38rpx" /> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view class="userview-info box admin"> |
|||
<view>* 姓名和电话信息需要管理员修改</view> |
|||
<view> |
|||
<text>管理员电话:</text> |
|||
<text class="phone">{{ amdinPhone }}</text> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
<u-popup :show="showEdit" mode="center" @close="showEdit = false" :round="10"> |
|||
<view class="popupView"> |
|||
<view class="title">修改{{ popFrom.label }}</view> |
|||
<view v-if="popFrom.key === 'sex'" class="flex-center-between sexview"> |
|||
<text>性别</text> |
|||
<u-radio-group v-model="popFrom.value" placement="row"> |
|||
<u-radio shape="circle" label="男" :name="1" /> |
|||
<u-radio shape="circle" label="女" :name="2" /> |
|||
</u-radio-group> |
|||
</view> |
|||
<u-input v-else :placeholder="`请输入${popFrom.label}`" input-align="right" v-model="popFrom.value"> |
|||
<template v-slot:prefix> |
|||
<text style="color: #909399">{{ popFrom.label }}</text> |
|||
</template> |
|||
</u-input> |
|||
|
|||
<view class="bts" @click="save">确认信息</view> |
|||
</view> |
|||
</u-popup> |
|||
</view> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.userview { |
|||
width: 100%; |
|||
padding: 32rpx; |
|||
min-height: 100vh; |
|||
box-sizing: border-box; |
|||
background-color: #f9fafb; |
|||
|
|||
.popupView { |
|||
width: 90vw; |
|||
padding: 20rpx 40rpx; |
|||
|
|||
.title { |
|||
font-size: 36rpx; |
|||
font-weight: 500; |
|||
margin-bottom: 40rpx; |
|||
} |
|||
|
|||
:deep(.u-input) { |
|||
border-width: 2rpx !important; |
|||
} |
|||
|
|||
.bts { |
|||
margin-top: 40rpx; |
|||
} |
|||
|
|||
.sexview { |
|||
:deep(.u-radio-group) { |
|||
justify-content: flex-end !important; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.box { |
|||
margin-bottom: 32rpx; |
|||
border-radius: 24rpx; |
|||
background-color: #ffffff; |
|||
box-shadow: 0 2rpx 4rpx 0 rgba(0, 0, 0, 0.05); |
|||
} |
|||
|
|||
&-search { |
|||
color: #4b5563; |
|||
font-size: 24rpx; |
|||
line-height: 40rpx; |
|||
padding: 46rpx 32rpx; |
|||
|
|||
&-label { |
|||
margin-right: 44rpx; |
|||
} |
|||
|
|||
&-bts { |
|||
color: #2563eb; |
|||
} |
|||
} |
|||
|
|||
&-info { |
|||
font-size: 28rpx; |
|||
font-weight: normal; |
|||
line-height: 40rpx; |
|||
padding: 0 32rpx 32rpx; |
|||
border: 1rpx solid #fff; |
|||
|
|||
&.active { |
|||
border-color: #2563eb; |
|||
} |
|||
|
|||
&.admin { |
|||
padding: 32rpx; |
|||
color: #6b7280; |
|||
margin: 0 0 60rpx; |
|||
|
|||
.phone { |
|||
color: #2563eb; |
|||
} |
|||
} |
|||
|
|||
&-item { |
|||
padding: 60rpx 0 24rpx; |
|||
border-top: 1rpx solid #e5e7eb; |
|||
|
|||
&:first-child { |
|||
border: none; |
|||
} |
|||
|
|||
&-label { |
|||
color: #4b5563; |
|||
} |
|||
|
|||
&-content { |
|||
color: #1f2937; |
|||
|
|||
> text { |
|||
margin-right: 16rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.bts { |
|||
color: #fff; |
|||
font-size: 28rpx; |
|||
line-height: 40rpx; |
|||
text-align: center; |
|||
border-radius: 8rpx; |
|||
padding: 24rpx 80rpx; |
|||
letter-spacing: 4rpx; |
|||
border: 1rpx solid #2563eb; |
|||
background-color: #2563eb; |
|||
|
|||
&.plain { |
|||
color: #2563eb; |
|||
background-color: #eff6ff; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,51 @@ |
|||
// import { getAuthorization } from '@/config'
|
|||
|
|||
// 白名单
|
|||
// const whiteList = ['/agentpages/index/index', '/agentpages/mine/index']
|
|||
|
|||
export default async function () { |
|||
const list = ['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'] |
|||
|
|||
// 用遍历的方式分别为,uni.navigateTo,uni.redirectTo,uni.reLaunch,uni.switchTab这4个路由方法添加拦截器
|
|||
list.forEach((item) => { |
|||
uni.addInterceptor(item, { |
|||
invoke(e) { |
|||
console.log('e', e) |
|||
|
|||
// 获取要跳转的页面路径(url去掉"?"和"?"后的参数)
|
|||
// const url = e.url.split('?')[0]
|
|||
// const type = url.split('/')[1] || ''
|
|||
|
|||
// let data
|
|||
// if (getAuthorization()) {
|
|||
// data = JSON.parse(getAuthorization())
|
|||
// } else {
|
|||
// data = { userInfo: { is_real: 0 } }
|
|||
// }
|
|||
|
|||
// // 判断当前窗口是白名单,如果是则不重定向路由
|
|||
// if (type === 'agentpages' && !whiteList.includes(url) && !data.userInfo.is_real) {
|
|||
// uni.showModal({
|
|||
// title: '提示',
|
|||
// content: '请先实名认证',
|
|||
// showCancel: true,
|
|||
// success({ confirm }) {
|
|||
// if (confirm) {
|
|||
// uni.navigateTo({
|
|||
// url: '/pages/mine/authentication'
|
|||
// })
|
|||
// }
|
|||
// }
|
|||
// })
|
|||
// return false
|
|||
// }
|
|||
|
|||
return e |
|||
}, |
|||
fail(err) { |
|||
// 失败回调拦截
|
|||
console.log(err) |
|||
} |
|||
}) |
|||
}) |
|||
} |
|||
@ -0,0 +1,257 @@ |
|||
<script setup lang="ts"> |
|||
import { shareApi } from '@/api/agent/promotion' |
|||
import { getHeaderImage } from '@/utils/common' |
|||
|
|||
const { screenWidth } = uni.getSystemInfoSync() |
|||
|
|||
const qrcodeRef = ref() |
|||
|
|||
const userInfo = ref<{ head_img: string; id: number; phone: string; nickname: string }>({ |
|||
head_img: '', |
|||
id: 0, |
|||
phone: '', |
|||
nickname: '' |
|||
}) |
|||
|
|||
const showSaveImgWin = ref(false) |
|||
|
|||
const canvasToTempFilePath = ref('') |
|||
|
|||
const oncomplete = ({ success }: { success: boolean }) => { |
|||
if (!success) return |
|||
qrcodeRef.value.toTempFilePath({ |
|||
success: ({ tempFilePath }: { tempFilePath: string }) => { |
|||
createCanvas(tempFilePath) |
|||
} |
|||
}) |
|||
} |
|||
|
|||
const saveShareImg = () => { |
|||
uni.saveImageToPhotosAlbum({ |
|||
filePath: canvasToTempFilePath.value, |
|||
success: () => { |
|||
showSaveImgWin.value = false |
|||
uni.showToast({ |
|||
title: '保存成功,快去分享到朋友圈吧~', |
|||
icon: 'none', |
|||
duration: 1000 |
|||
}) |
|||
}, |
|||
fail: () => { |
|||
uni.showToast({ |
|||
title: '保存失败,请检查是否授权保存图片权限~', |
|||
icon: 'none', |
|||
duration: 1000 |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
|
|||
const createCanvas = (tempFilePath: string) => { |
|||
uni.getImageInfo({ |
|||
src: getHeaderImage(userInfo.value.head_img), |
|||
success: ({ path }) => { |
|||
const ctx = uni.createCanvasContext('shareCanvas', this) |
|||
const canvasWidth = screenWidth - 30 |
|||
const canvasHeight = (screenWidth - 30) * 1.62 |
|||
|
|||
ctx.translate(0, 0) |
|||
ctx.beginPath() |
|||
ctx.drawImage('../../static/invite.jpg', 0, 0, canvasWidth, canvasHeight) |
|||
ctx.restore() |
|||
|
|||
ctx.beginPath() |
|||
ctx.translate(0, canvasHeight - 78) |
|||
ctx.beginPath() |
|||
ctx.setFillStyle('rgba(0, 0, 0, 0.24)') |
|||
ctx.fillRect(0, 0, canvasWidth, 78) |
|||
ctx.save() |
|||
|
|||
ctx.save() |
|||
ctx.beginPath() |
|||
ctx.arc(40, 40, 22, 0, 2 * Math.PI, false) |
|||
ctx.setFillStyle('rgba(0, 0, 0, 0.29)') |
|||
ctx.clip() |
|||
ctx.drawImage(path, 19, 19, 42, 42) |
|||
ctx.restore() |
|||
|
|||
ctx.font = 'normal normal 13px sans-serif' |
|||
ctx.setFillStyle('#ffffff') |
|||
ctx.fillText('用户昵称', 71, 37) |
|||
|
|||
ctx.font = 'normal normal 12px sans-serif' |
|||
ctx.setFillStyle('#ffffff') |
|||
ctx.fillText(`${userInfo.value.nickname}`, 71, 54) |
|||
ctx.save() |
|||
|
|||
ctx.beginPath() |
|||
const x = canvasWidth - 78, |
|||
y = 7, |
|||
width = 64, |
|||
height = 64, |
|||
radius = 4 |
|||
ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 1.5) |
|||
ctx.lineTo(x + width - radius, y) |
|||
ctx.arc(x + width - radius, y + radius, radius, Math.PI * 1.5, Math.PI * 2) |
|||
ctx.lineTo(x + width, y + height - radius) |
|||
ctx.arc(x + width - radius, y + height - radius, radius, 0, Math.PI * 0.5) |
|||
ctx.lineTo(x + radius, y + height) |
|||
ctx.arc(x + radius, y + height - radius, radius, Math.PI * 0.5, Math.PI) |
|||
ctx.lineTo(x, y + radius) |
|||
ctx.setFillStyle('#ffffff') |
|||
ctx.fill() |
|||
ctx.closePath() |
|||
|
|||
ctx.beginPath() |
|||
ctx.drawImage(tempFilePath, x + 3, y + 3, 58, 58) |
|||
ctx.restore() |
|||
|
|||
ctx.draw(false, () => { |
|||
uni.canvasToTempFilePath({ |
|||
width: canvasWidth, |
|||
height: canvasHeight, |
|||
destWidth: canvasWidth * 2, |
|||
destHeight: canvasHeight * 2, |
|||
canvasId: 'shareCanvas', |
|||
quality: 1, |
|||
success: ({ tempFilePath }) => { |
|||
canvasToTempFilePath.value = tempFilePath |
|||
}, |
|||
complete: () => { |
|||
uni.hideLoading() |
|||
setTimeout(() => uni.hideToast(), 1000) |
|||
} |
|||
}) |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
|
|||
// const shareToFriends = () => { |
|||
// plus.share.sendWithSystem( |
|||
// { content: '分享内容', href: 'https://www.dcloud.io/' }, |
|||
// function () { |
|||
// console.log('分享成功') |
|||
// }, |
|||
// function (e) { |
|||
// console.log('分享失败:' + JSON.stringify(e)) |
|||
// } |
|||
// ) |
|||
// } |
|||
|
|||
onLoad(() => { |
|||
uni.showLoading({ title: '加载中...' }) |
|||
shareApi().then((res) => { |
|||
const { data } = res as { data: { head_img: string; id: number; phone: string; nickname: string } } |
|||
userInfo.value = data |
|||
qrcodeRef.value.make() |
|||
}) |
|||
}) |
|||
</script> |
|||
|
|||
<template> |
|||
<view class="invite"> |
|||
<ex-header title="邀请好友" /> |
|||
|
|||
<uv-qrcode |
|||
ref="qrcodeRef" |
|||
class="qrcode" |
|||
size="174rpx" |
|||
:value="`http://pos-admin.lingji.vip/#/sign?p=${userInfo.phone}&t=a`" |
|||
@complete="oncomplete" |
|||
/> |
|||
|
|||
<canvas canvas-id="shareCanvas" :style="{ width: `${screenWidth - 30}px`, height: `${(screenWidth - 30) * 1.62}px` }" hidpi /> |
|||
<image @longpress="showSaveImgWin = true" style="width: 100%; height: 100%" :src="canvasToTempFilePath" /> |
|||
|
|||
<view class="option-box flex-center-between"> |
|||
<view class="btn" @click="showSaveImgWin = true">保存图片</view> |
|||
|
|||
<!-- <view class="option-box-item"> |
|||
<view class="img-box"> |
|||
<image src="http://cdn-pos.lingji.vip/static/share/circle_of_friends.png" mode="widthFix" /> |
|||
</view> |
|||
<view class="label">朋友圈</view> |
|||
</view> |
|||
<view class="option-box-item" @click="shareToFriends()"> |
|||
<view class="img-box"> |
|||
<image src="http://cdn-pos.lingji.vip/static/share/wechart.png" mode="widthFix" /> |
|||
</view> |
|||
<view class="label">微信</view> |
|||
</view> |
|||
<view class="option-box-item" @click="downloadFn()"> |
|||
<view class="img-box"> |
|||
<image src="http://cdn-pos.lingji.vip/static/share/download.png" mode="widthFix" /> |
|||
</view> |
|||
<view class="label">保存</view> |
|||
</view> --> |
|||
</view> |
|||
|
|||
<u-modal |
|||
:show="showSaveImgWin" |
|||
show-cancel-button |
|||
close-on-click-overlay |
|||
@cancel="showSaveImgWin = false" |
|||
@close="showSaveImgWin = false" |
|||
@confirm="saveShareImg" |
|||
> |
|||
确定要保存图片吗? |
|||
</u-modal> |
|||
</view> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
.invite { |
|||
width: 100vw; |
|||
min-height: 100vh; |
|||
padding: 30rpx; |
|||
|
|||
.qrcode { |
|||
position: absolute; |
|||
top: -100%; |
|||
} |
|||
|
|||
.option-box { |
|||
padding: 46rpx 37rpx 102rpx; |
|||
|
|||
&-item { |
|||
.img-box { |
|||
@extend .flex; |
|||
width: 104rpx; |
|||
height: 104rpx; |
|||
overflow: hidden; |
|||
border-radius: 50%; |
|||
margin-bottom: 10rpx; |
|||
background-color: #f2f6ff; |
|||
|
|||
image { |
|||
width: 66rpx; |
|||
} |
|||
} |
|||
|
|||
.label { |
|||
width: 100%; |
|||
color: #000; |
|||
font-size: 28rpx; |
|||
font-weight: 400; |
|||
letter-spacing: 0; |
|||
line-height: 40rpx; |
|||
text-align: center; |
|||
} |
|||
} |
|||
|
|||
.btn { |
|||
width: 100%; |
|||
color: #ffffff; |
|||
font-size: 32rpx; |
|||
font-weight: 500; |
|||
letter-spacing: 0; |
|||
line-height: 40rpx; |
|||
text-align: center; |
|||
padding: 20rpx 0; |
|||
border-radius: 8rpx; |
|||
background: linear-gradient(90deg, #4778ff 0%, #4778ffb8 100%); |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 923 B |
|
After Width: | Height: | Size: 750 B |
|
After Width: | Height: | Size: 918 B |
|
After Width: | Height: | Size: 1005 B |
@ -0,0 +1,10 @@ |
|||
import { createPinia } from 'pinia' |
|||
import { createUnistorage } from 'pinia-plugin-unistorage' |
|||
|
|||
const store = createPinia() |
|||
// 使用该插件
|
|||
// 关键代码 👇
|
|||
store.use(createUnistorage()) |
|||
|
|||
//导出
|
|||
export default store |
|||
@ -0,0 +1,115 @@ |
|||
// 定义组合式API仓库
|
|||
import { defineStore } from 'pinia' |
|||
import { getPrefixName } from '@/config' |
|||
import { getOpenid } from '@/api/login' |
|||
import { infoForOpenid, bind as bindApi } from '@/api/user' |
|||
|
|||
interface userInfoStoreInt { |
|||
[n: string]: any |
|||
} |
|||
export default defineStore( |
|||
getPrefixName('user'), |
|||
() => { |
|||
const openId = ref('') |
|||
const mobile = ref('') |
|||
const meetId = ref('') |
|||
const showtoast = ref(false) |
|||
const userInfo = ref<userInfoStoreInt>({}) |
|||
|
|||
function getopenid(params: { code: string }) { |
|||
return new Promise<any>((resolve, reject) => { |
|||
getOpenid(params) |
|||
.then((res) => { |
|||
const { data } = res as { data: string } |
|||
|
|||
openId.value = data |
|||
|
|||
resolve({ code: 1, message: '登录成功~' }) |
|||
}) |
|||
.catch((err) => { |
|||
reject(err) |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
function getUserInfo() { |
|||
return new Promise<any>( (resolve, reject) => { |
|||
infoForOpenid({ openid: openId.value, mobile: mobile.value, meetId: meetId.value}) |
|||
.then((res) => { |
|||
const { data } = res as { data: userInfoStoreInt } |
|||
userInfo.value = data || {} |
|||
resolve({ code: 1, data, message: 'SUCCESS' }) |
|||
}) |
|||
.catch((err) => { |
|||
reject(err) |
|||
}) |
|||
|
|||
}) |
|||
} |
|||
|
|||
function bindUser(id: number) { |
|||
return new Promise<any>((resolve, reject) => { |
|||
bindApi({ id, openid: openId.value }) |
|||
.then(() => { |
|||
resolve({ code: 1, message: 'SUCCESS' }) |
|||
}) |
|||
.catch((err) => { |
|||
reject(err) |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
function logOut() { |
|||
userInfo.value = {} |
|||
uni.clearStorageSync() |
|||
|
|||
uni.showToast({ |
|||
icon: 'none', |
|||
title: '退出成功', |
|||
mask: true, |
|||
success() { |
|||
setTimeout(() => uni.reLaunch({ url: 'pages/login/login' }), 1000) |
|||
} |
|||
}) |
|||
} |
|||
|
|||
return { |
|||
openId, |
|||
mobile, |
|||
meetId, |
|||
showtoast, |
|||
userInfo, |
|||
getopenid, |
|||
bindUser, |
|||
getUserInfo, |
|||
logOut |
|||
} |
|||
}, |
|||
{ |
|||
unistorage: { |
|||
serializer: { |
|||
// 序列化,默认为 JSON.stringify
|
|||
serialize(v) { |
|||
return JSON.stringify(v) |
|||
}, |
|||
// 反序列化,默认为 JSON.parse
|
|||
deserialize(v) { |
|||
return JSON.parse(v) |
|||
} |
|||
} |
|||
} // 开启后对 state 的数据读写都将持久化
|
|||
// unistorage: {
|
|||
// key: 'userInfo', // 缓存的键,默认为该 store 的 id,这里是 main,
|
|||
// paths: ['userInfo.token'], // 需要缓存的路径,这里设置 foo 和 nested 下的 data 会被缓存
|
|||
// // 初始化恢复前触发
|
|||
// beforeRestore(ctx: any) {
|
|||
// console.log(ctx)
|
|||
// },
|
|||
// // 初始化恢复后触发
|
|||
// afterRestore(ctx: any) {
|
|||
// console.log('ctx', ctx)
|
|||
// },
|
|||
|
|||
// },
|
|||
} |
|||
) |
|||
@ -0,0 +1,180 @@ |
|||
/* eslint-disable */ |
|||
/* prettier-ignore */ |
|||
// @ts-nocheck
|
|||
// noinspection JSUnusedGlobalSymbols
|
|||
// Generated by unplugin-auto-import
|
|||
export {} |
|||
declare global { |
|||
const EffectScope: typeof import('vue')['EffectScope'] |
|||
const computed: typeof import('vue')['computed'] |
|||
const createApp: typeof import('vue')['createApp'] |
|||
const customRef: typeof import('vue')['customRef'] |
|||
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] |
|||
const defineComponent: typeof import('vue')['defineComponent'] |
|||
const effectScope: typeof import('vue')['effectScope'] |
|||
const getCurrentInstance: typeof import('vue')['getCurrentInstance'] |
|||
const getCurrentScope: typeof import('vue')['getCurrentScope'] |
|||
const h: typeof import('vue')['h'] |
|||
const inject: typeof import('vue')['inject'] |
|||
const isProxy: typeof import('vue')['isProxy'] |
|||
const isReactive: typeof import('vue')['isReactive'] |
|||
const isReadonly: typeof import('vue')['isReadonly'] |
|||
const isRef: typeof import('vue')['isRef'] |
|||
const markRaw: typeof import('vue')['markRaw'] |
|||
const nextTick: typeof import('vue')['nextTick'] |
|||
const onActivated: typeof import('vue')['onActivated'] |
|||
const onAddToFavorites: typeof import('@dcloudio/uni-app')['onAddToFavorites'] |
|||
const onBackPress: typeof import('@dcloudio/uni-app')['onBackPress'] |
|||
const onBeforeMount: typeof import('vue')['onBeforeMount'] |
|||
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] |
|||
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] |
|||
const onDeactivated: typeof import('vue')['onDeactivated'] |
|||
const onError: typeof import('@dcloudio/uni-app')['onError'] |
|||
const onErrorCaptured: typeof import('vue')['onErrorCaptured'] |
|||
const onHide: typeof import('@dcloudio/uni-app')['onHide'] |
|||
const onLaunch: typeof import('@dcloudio/uni-app')['onLaunch'] |
|||
const onLoad: typeof import('@dcloudio/uni-app')['onLoad'] |
|||
const onMounted: typeof import('vue')['onMounted'] |
|||
const onNavigationBarButtonTap: typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap'] |
|||
const onNavigationBarSearchInputChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged'] |
|||
const onNavigationBarSearchInputClicked: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked'] |
|||
const onNavigationBarSearchInputConfirmed: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed'] |
|||
const onNavigationBarSearchInputFocusChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged'] |
|||
const onPageNotFound: typeof import('@dcloudio/uni-app')['onPageNotFound'] |
|||
const onPageScroll: typeof import('@dcloudio/uni-app')['onPageScroll'] |
|||
const onPullDownRefresh: typeof import('@dcloudio/uni-app')['onPullDownRefresh'] |
|||
const onReachBottom: typeof import('@dcloudio/uni-app')['onReachBottom'] |
|||
const onReady: typeof import('@dcloudio/uni-app')['onReady'] |
|||
const onRenderTracked: typeof import('vue')['onRenderTracked'] |
|||
const onRenderTriggered: typeof import('vue')['onRenderTriggered'] |
|||
const onResize: typeof import('@dcloudio/uni-app')['onResize'] |
|||
const onScopeDispose: typeof import('vue')['onScopeDispose'] |
|||
const onServerPrefetch: typeof import('vue')['onServerPrefetch'] |
|||
const onShareAppMessage: typeof import('@dcloudio/uni-app')['onShareAppMessage'] |
|||
const onShareTimeline: typeof import('@dcloudio/uni-app')['onShareTimeline'] |
|||
const onShow: typeof import('@dcloudio/uni-app')['onShow'] |
|||
const onTabItemTap: typeof import('@dcloudio/uni-app')['onTabItemTap'] |
|||
const onThemeChange: typeof import('@dcloudio/uni-app')['onThemeChange'] |
|||
const onUnhandledRejection: typeof import('@dcloudio/uni-app')['onUnhandledRejection'] |
|||
const onUnload: typeof import('@dcloudio/uni-app')['onUnload'] |
|||
const onUnmounted: typeof import('vue')['onUnmounted'] |
|||
const onUpdated: typeof import('vue')['onUpdated'] |
|||
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] |
|||
const provide: typeof import('vue')['provide'] |
|||
const reactive: typeof import('vue')['reactive'] |
|||
const readonly: typeof import('vue')['readonly'] |
|||
const ref: typeof import('vue')['ref'] |
|||
const resolveComponent: typeof import('vue')['resolveComponent'] |
|||
const shallowReactive: typeof import('vue')['shallowReactive'] |
|||
const shallowReadonly: typeof import('vue')['shallowReadonly'] |
|||
const shallowRef: typeof import('vue')['shallowRef'] |
|||
const toRaw: typeof import('vue')['toRaw'] |
|||
const toRef: typeof import('vue')['toRef'] |
|||
const toRefs: typeof import('vue')['toRefs'] |
|||
const toValue: typeof import('vue')['toValue'] |
|||
const triggerRef: typeof import('vue')['triggerRef'] |
|||
const unref: typeof import('vue')['unref'] |
|||
const useAttrs: typeof import('vue')['useAttrs'] |
|||
const useCssModule: typeof import('vue')['useCssModule'] |
|||
const useCssVars: typeof import('vue')['useCssVars'] |
|||
const useId: typeof import('vue')['useId'] |
|||
const useModel: typeof import('vue')['useModel'] |
|||
const useSlots: typeof import('vue')['useSlots'] |
|||
const useTemplateRef: typeof import('vue')['useTemplateRef'] |
|||
const watch: typeof import('vue')['watch'] |
|||
const watchEffect: typeof import('vue')['watchEffect'] |
|||
const watchPostEffect: typeof import('vue')['watchPostEffect'] |
|||
const watchSyncEffect: typeof import('vue')['watchSyncEffect'] |
|||
} |
|||
// for type re-export
|
|||
declare global { |
|||
// @ts-ignore
|
|||
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' |
|||
import('vue') |
|||
} |
|||
// for vue template auto import
|
|||
import { UnwrapRef } from 'vue' |
|||
declare module 'vue' { |
|||
interface ComponentCustomProperties { |
|||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']> |
|||
readonly computed: UnwrapRef<typeof import('vue')['computed']> |
|||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']> |
|||
readonly customRef: UnwrapRef<typeof import('vue')['customRef']> |
|||
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']> |
|||
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']> |
|||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']> |
|||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']> |
|||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']> |
|||
readonly h: UnwrapRef<typeof import('vue')['h']> |
|||
readonly inject: UnwrapRef<typeof import('vue')['inject']> |
|||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']> |
|||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']> |
|||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']> |
|||
readonly isRef: UnwrapRef<typeof import('vue')['isRef']> |
|||
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']> |
|||
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']> |
|||
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']> |
|||
readonly onAddToFavorites: UnwrapRef<typeof import('@dcloudio/uni-app')['onAddToFavorites']> |
|||
readonly onBackPress: UnwrapRef<typeof import('@dcloudio/uni-app')['onBackPress']> |
|||
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']> |
|||
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']> |
|||
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']> |
|||
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']> |
|||
readonly onError: UnwrapRef<typeof import('@dcloudio/uni-app')['onError']> |
|||
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']> |
|||
readonly onHide: UnwrapRef<typeof import('@dcloudio/uni-app')['onHide']> |
|||
readonly onLaunch: UnwrapRef<typeof import('@dcloudio/uni-app')['onLaunch']> |
|||
readonly onLoad: UnwrapRef<typeof import('@dcloudio/uni-app')['onLoad']> |
|||
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']> |
|||
readonly onNavigationBarButtonTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']> |
|||
readonly onNavigationBarSearchInputChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']> |
|||
readonly onNavigationBarSearchInputClicked: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']> |
|||
readonly onNavigationBarSearchInputConfirmed: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']> |
|||
readonly onNavigationBarSearchInputFocusChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']> |
|||
readonly onPageNotFound: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageNotFound']> |
|||
readonly onPageScroll: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageScroll']> |
|||
readonly onPullDownRefresh: UnwrapRef<typeof import('@dcloudio/uni-app')['onPullDownRefresh']> |
|||
readonly onReachBottom: UnwrapRef<typeof import('@dcloudio/uni-app')['onReachBottom']> |
|||
readonly onReady: UnwrapRef<typeof import('@dcloudio/uni-app')['onReady']> |
|||
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']> |
|||
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']> |
|||
readonly onResize: UnwrapRef<typeof import('@dcloudio/uni-app')['onResize']> |
|||
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']> |
|||
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']> |
|||
readonly onShareAppMessage: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareAppMessage']> |
|||
readonly onShareTimeline: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareTimeline']> |
|||
readonly onShow: UnwrapRef<typeof import('@dcloudio/uni-app')['onShow']> |
|||
readonly onTabItemTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onTabItemTap']> |
|||
readonly onThemeChange: UnwrapRef<typeof import('@dcloudio/uni-app')['onThemeChange']> |
|||
readonly onUnhandledRejection: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnhandledRejection']> |
|||
readonly onUnload: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnload']> |
|||
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']> |
|||
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']> |
|||
readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']> |
|||
readonly provide: UnwrapRef<typeof import('vue')['provide']> |
|||
readonly reactive: UnwrapRef<typeof import('vue')['reactive']> |
|||
readonly readonly: UnwrapRef<typeof import('vue')['readonly']> |
|||
readonly ref: UnwrapRef<typeof import('vue')['ref']> |
|||
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']> |
|||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']> |
|||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']> |
|||
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']> |
|||
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']> |
|||
readonly toRef: UnwrapRef<typeof import('vue')['toRef']> |
|||
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']> |
|||
readonly toValue: UnwrapRef<typeof import('vue')['toValue']> |
|||
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']> |
|||
readonly unref: UnwrapRef<typeof import('vue')['unref']> |
|||
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']> |
|||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']> |
|||
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']> |
|||
readonly useId: UnwrapRef<typeof import('vue')['useId']> |
|||
readonly useModel: UnwrapRef<typeof import('vue')['useModel']> |
|||
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']> |
|||
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']> |
|||
readonly watch: UnwrapRef<typeof import('vue')['watch']> |
|||
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']> |
|||
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']> |
|||
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']> |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
/// <reference types="vite/client" />
|
|||
/// <reference types="@dcloudio/types/index" />
|
|||
/// <reference types="uview-plus/types/index" />
|
|||
/// <reference types="feng-uniapp-exploit/types/index" />
|
|||
|
|||
declare module '*.vue' { |
|||
import { DefineComponent } from 'vue' |
|||
const component: DefineComponent<{}, {}, any> |
|||
export default component |
|||
} |
|||
|
|||
declare module 'uview-plus' |
|||
declare module 'feng-uniapp-exploit' |
|||
@ -0,0 +1,29 @@ |
|||
/* eslint-disable prettier/prettier */ |
|||
|
|||
declare global { |
|||
type useType = 'client' | 'agent' |
|||
|
|||
/** |
|||
* 公共的路径 |
|||
* @author shenname <shenname@163.com> |
|||
* @license MIT |
|||
* @param { string } fileName 文件名称 |
|||
* @param { string } filePath 文件路径 |
|||
* @example |
|||
* fileName: images.png |
|||
* filePath: images |
|||
*/ |
|||
interface imgUrlInt { |
|||
fileName: string |
|||
filePath?: string |
|||
} |
|||
|
|||
interface pageType { |
|||
page: number |
|||
limit: number |
|||
total?: number |
|||
} |
|||
|
|||
} |
|||
|
|||
export { } |
|||
@ -0,0 +1,118 @@ |
|||
@import 'uview-plus/theme.scss'; |
|||
|
|||
#app { |
|||
box-sizing: border-box; |
|||
background-color: #f5f5f5 |
|||
} |
|||
|
|||
/* 颜色变量 */ |
|||
|
|||
/*============================= 文字尺寸 =============================*/ |
|||
$font-size-20: 20rpx; |
|||
$font-size-22: 22rpx; |
|||
$font-size-24: 24rpx; |
|||
$font-size-26: 26rpx; |
|||
$font-size-28: 28rpx; |
|||
$font-size-30: 30rpx; |
|||
$font-size-32: 32rpx; |
|||
$font-size-34: 34rpx; |
|||
$font-size-36: 36rpx; |
|||
$font-size-40: 40rpx; |
|||
|
|||
image { |
|||
width: 100%; |
|||
height: 100%; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
view { |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
/*============================= 弹性盒子 =============================*/ |
|||
%flex-base { |
|||
display: flex; |
|||
flex-wrap: nowrap; |
|||
} |
|||
|
|||
$flex-way: ( |
|||
start, |
|||
flex-start), |
|||
(center, center), |
|||
(end, flex-end), |
|||
(between, space-between), |
|||
(around, space-around), |
|||
(evenly, space-evenly |
|||
); |
|||
|
|||
@mixin flex-algin($align) { |
|||
@each $way, $justify in $flex-way { |
|||
&-#{$way} { |
|||
@if $way !=$align { |
|||
@if $way !=$align { |
|||
@extend %flex-base; |
|||
align-items: $align; |
|||
justify-content: $justify; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.flex { |
|||
@extend %flex-base; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
@each $way, $justify in (start, flex-start), (center, center), (end, flex-end) { |
|||
&-#{$way} { |
|||
@include flex-algin($justify); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[class*="flex-"], |
|||
[class^="flex"] { |
|||
&.nowrap { |
|||
flex-wrap: nowrap; |
|||
} |
|||
|
|||
&.stretch { |
|||
align-items: stretch; |
|||
} |
|||
|
|||
@each $direction, $name in (row, direction), (column, direction), (wrap, wrap) { |
|||
&.#{$direction} { |
|||
flex-#{$name}: $direction; |
|||
|
|||
&-reverse { |
|||
flex-#{$name}: '#{$direction}-reverse'; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
@for $i from 1 through 4 { |
|||
.flex#{$i} { |
|||
flex: $i; |
|||
} |
|||
} |
|||
|
|||
/*============================= 文字溢出 =============================*/ |
|||
.text-ellipsis { |
|||
overflow: hidden; |
|||
white-space: nowrap; |
|||
text-overflow: ellipsis; |
|||
|
|||
@for $i from 2 through 4 { |
|||
&-#{$i} { |
|||
overflow: hidden; |
|||
white-space: normal; |
|||
text-overflow: ellipsis; |
|||
display: -webkit-box; |
|||
-webkit-box-orient: vertical; |
|||
line-clamp: 1; |
|||
-webkit-line-clamp: $i; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
/** |
|||
* 获取用户头像地址 |
|||
* @author shenname <shenname@163.com> |
|||
* @license MIT |
|||
* @param { string } url 头像地址 |
|||
* @returns { string } |
|||
*/ |
|||
export const getHeaderImage = (url?: string) => { |
|||
if (url) { |
|||
return import.meta.env.VITE_APP_BASE_URL + url |
|||
} else { |
|||
return 'http://cdn-pos.lingji.vip/static/system/headimg.png' |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取图片地址 |
|||
* @author shenname <shenname@163.com> |
|||
* @license MIT |
|||
* @param { string } url 图片地址 |
|||
* @returns { string } |
|||
*/ |
|||
export const getImageUrl = (url?: string) => { |
|||
return import.meta.env.VITE_APP_BASE_URL + url |
|||
} |
|||
|
|||
/** |
|||
* 获取文件相对路径、URL |
|||
* @author shenname <shenname@163.com> |
|||
* @license MIT |
|||
* @param { string } fileName 文件名称 | 相对路径 |
|||
* @param { string } [filePath=images] filePath 文件在 /static 下的路径 获取URL时不传 |
|||
* @example |
|||
* imgUrl('a.png') |
|||
* imgUrl('a.png', '/public/') |
|||
* imgUrl('/upload/images/img.png') |
|||
* @returns {string} |
|||
*/ |
|||
export const fileUrl = (fileName: string, filePath: string = 'images'): string => { |
|||
if (/[ `!@#$%^&*()_+\-=\[\]{};':"\\|,<>\/?~]/.test(fileName)) { |
|||
return import.meta.env.VITE_APP_BASE_URL + fileName |
|||
} else { |
|||
return `http://cdn-pos.lingji.vip/static/${filePath}/${fileName}` |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 判断对象是否为空 |
|||
* @author shenname <shenname@163.com> |
|||
* @license MIT |
|||
* @param any |
|||
* @returns {boolean} |
|||
*/ |
|||
export const isEmptyObject = (obj: any): boolean => { |
|||
if (obj == null) return true |
|||
if (typeof obj !== 'object' || Array.isArray(obj)) return true |
|||
return Reflect.ownKeys(obj).length === 0 |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
interface ResponseOptions { |
|||
url: string |
|||
headers?: { [key: string]: string } |
|||
method?: 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT' |
|||
data?: { [key: string]: any } |
|||
isSinglePost?: boolean |
|||
} |
|||
|
|||
export const request = { |
|||
isLock: false, |
|||
http({ url = '', headers = {}, data = {}, method = 'POST', isSinglePost = false }: ResponseOptions) { |
|||
const _this = this |
|||
|
|||
return new Promise(function (resolve, reject) { |
|||
if (isSinglePost && _this.isLock) { |
|||
reject({ message: '加载中' }) |
|||
} |
|||
_this.isLock = true |
|||
|
|||
// #ifdef APP-PLUS
|
|||
url = import.meta.env.VITE_APP_BASE_URL + url |
|||
// #endif
|
|||
// #ifdef H5
|
|||
url = import.meta.env.VITE_APP_BASE_PRE + url |
|||
// #endif
|
|||
|
|||
// #ifdef MP-WEIXIN
|
|||
url = import.meta.env.VITE_APP_BASE_URL + url |
|||
// #endif
|
|||
|
|||
const header = Object.assign({ 'content-type': 'application/json', Authorization: '' }, headers) |
|||
|
|||
uni.request({ |
|||
url, |
|||
header, |
|||
method, |
|||
data, |
|||
success(res) { |
|||
const data = res.data as { code: number; data: object; msg: string } |
|||
|
|||
switch (data.code) { |
|||
case 1: |
|||
resolve(res.data) |
|||
break |
|||
default: |
|||
uni.showToast({ title: data.msg, icon: 'none', mask: true }) |
|||
reject(res.data) |
|||
break |
|||
} |
|||
}, |
|||
fail(err) { |
|||
uni.showToast({ title: '网络错误', icon: 'none' }) |
|||
reject(err) |
|||
}, |
|||
complete() { |
|||
_this.isLock = false |
|||
} |
|||
}) |
|||
}) |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
// 验证密码
|
|||
export function checkPassword(password: string) { |
|||
if (password.trim() === '') return false |
|||
|
|||
return /^(?=.*\d)(?=.*[a-zA-Z])(?=.*[^a-zA-Z0-9]).{8,16}$/.test(password) |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
{ |
|||
"extends": "@vue/tsconfig/tsconfig.json", |
|||
"compilerOptions": { |
|||
"sourceMap": true, |
|||
"baseUrl": ".", |
|||
"paths": { |
|||
"@/*": [ |
|||
"./src/*" |
|||
] |
|||
}, |
|||
"lib": [ |
|||
"esnext", |
|||
"dom" |
|||
], |
|||
"types": [ |
|||
"vite/client", |
|||
"@dcloudio/types", |
|||
], |
|||
"typeRoots": [ |
|||
"src/**/*.d.ts" |
|||
] |
|||
}, |
|||
"include": [ |
|||
"src/**/*.ts", |
|||
"src/**/*.tsx", |
|||
"src/**/*.vue", |
|||
"src/types/*.d.ts" |
|||
] |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
import { defineConfig, loadEnv } from 'vite' |
|||
import uni from '@dcloudio/vite-plugin-uni' |
|||
import AutoImport from 'unplugin-auto-import/vite' |
|||
|
|||
import path from 'path' |
|||
const pathSrc = path.resolve(__dirname, 'src') |
|||
|
|||
// https://vitejs.dev/config/
|
|||
export default defineConfig(({ mode }) => { |
|||
const { VITE_APP_BASE_URL, VITE_APP_BASE_PRE } = loadEnv(mode, process.cwd()) |
|||
|
|||
return { |
|||
transpileDependencies: ['uview-plus'], |
|||
resolve: { |
|||
// 别名
|
|||
alias: { |
|||
'@': path.join(__dirname, './src') |
|||
} |
|||
}, |
|||
plugins: [ |
|||
uni(), |
|||
AutoImport({ |
|||
// imports: ['vue', 'uni-app', { 'feng-uniapp-exploit': ['default'] }, { 'src/utils': ['default'] }],
|
|||
imports: ['vue', 'uni-app'], |
|||
vueTemplate: true, |
|||
dts: path.resolve(pathSrc, 'types', 'auto-imports.d.ts') |
|||
}) |
|||
], |
|||
// build: {
|
|||
// rollupOptions: {
|
|||
// external: ['feng-exploit-plus']
|
|||
// }
|
|||
// },
|
|||
server: { |
|||
proxy: { |
|||
[VITE_APP_BASE_PRE]: { |
|||
target: VITE_APP_BASE_URL, |
|||
ws: true, |
|||
changeOrigin: true, |
|||
rewrite: (path) => path.replace(new RegExp('^' + VITE_APP_BASE_PRE), '') |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}) |
|||