Browse Source

初始化

master
岳鹏龙 12 months ago
parent
commit
0019102482
  1. 5
      .env.development
  2. 6
      .env.production
  3. 3
      .eslintignore
  4. 146
      .eslintrc.cjs
  5. 30
      .gitignore
  6. 4
      .husky/pre-commit
  7. 8
      .prettierignore
  8. 36
      .prettierrc.cjs
  9. 85
      auto/addPage.ts
  10. 4
      auto/preinstall.js
  11. 23
      index.html
  12. 100
      package.json
  13. 29
      src/App.vue
  14. 25
      src/api/common.ts
  15. 39
      src/api/login.ts
  16. 61
      src/api/user.ts
  17. 44
      src/api/votingElection.ts
  18. 13
      src/config/index.ts
  19. 16
      src/main.ts
  20. 131
      src/manifest.json
  21. 85
      src/pages.json
  22. 283
      src/pages/index/index.vue
  23. 27
      src/pages/learningCenter/index.vue
  24. 111
      src/pages/login/login.vue
  25. 203
      src/pages/memberCenter/index.vue
  26. 331
      src/pages/mine/index.vue
  27. 51
      src/permission.ts
  28. 257
      src/share.vue
  29. BIN
      src/static/img/Group.png
  30. BIN
      src/static/logo.png
  31. BIN
      src/static/tabbar/grzx.png
  32. BIN
      src/static/tabbar/grzx_select.png
  33. BIN
      src/static/tabbar/hyzx.png
  34. BIN
      src/static/tabbar/hyzx_select.png
  35. BIN
      src/static/tabbar/index.png
  36. BIN
      src/static/tabbar/index_select.png
  37. BIN
      src/static/tabbar/xxzx.png
  38. BIN
      src/static/tabbar/xxzx_select.png
  39. 10
      src/store/index.ts
  40. 115
      src/store/user.ts
  41. 180
      src/types/auto-imports.d.ts
  42. 13
      src/types/env.d.ts
  43. 29
      src/types/global.d.ts
  44. 118
      src/uni.scss
  45. 58
      src/utils/common.ts
  46. 61
      src/utils/http.ts
  47. 6
      src/utils/rules.ts
  48. 29
      tsconfig.json
  49. 45
      vite.config.ts

5
.env.development

@ -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'

6
.env.production

@ -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'

3
.eslintignore

@ -0,0 +1,3 @@
dist
node_modules
uni_modules

146
.eslintrc.cjs

@ -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'
}
]
}
}

30
.gitignore

@ -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

4
.husky/pre-commit

@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm run fix

8
.prettierignore

@ -0,0 +1,8 @@
/dist/*
/html/*
.local
/node_modules/**
**/*.svg
**/*.sh
/public/*
/uni_modules/*

36
.prettierrc.cjs

@ -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'
}

85
auto/addPage.ts

@ -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);

4
auto/preinstall.js

@ -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)
}

23
index.html

@ -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>

100
package.json

@ -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"
}
}

29
src/App.vue

@ -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>

25
src/api/common.ts

@ -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
})
}

39
src/api/login.ts

@ -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
})
}

61
src/api/user.ts

@ -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
})
}

44
src/api/votingElection.ts

@ -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
})
}

13
src/config/index.ts

@ -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'))
}

16
src/main.ts

@ -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 }
}

131
src/manifest.json

@ -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"
}

85
src/pages.json

@ -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": "个人中心"
}
]
}
}

283
src/pages/index/index.vue

@ -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>

27
src/pages/learningCenter/index.vue

@ -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>

111
src/pages/login/login.vue

@ -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>

203
src/pages/memberCenter/index.vue

@ -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>

331
src/pages/mine/index.vue

@ -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>

51
src/permission.ts

@ -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)
}
})
})
}

257
src/share.vue

@ -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>

BIN
src/static/img/Group.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
src/static/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
src/static/tabbar/grzx.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/static/tabbar/grzx_select.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/static/tabbar/hyzx.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
src/static/tabbar/hyzx_select.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
src/static/tabbar/index.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

BIN
src/static/tabbar/index_select.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

BIN
src/static/tabbar/xxzx.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 B

BIN
src/static/tabbar/xxzx_select.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

10
src/store/index.ts

@ -0,0 +1,10 @@
import { createPinia } from 'pinia'
import { createUnistorage } from 'pinia-plugin-unistorage'
const store = createPinia()
// 使用该插件
// 关键代码 👇
store.use(createUnistorage())
//导出
export default store

115
src/store/user.ts

@ -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)
// },
// },
}
)

180
src/types/auto-imports.d.ts

@ -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']>
}
}

13
src/types/env.d.ts

@ -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'

29
src/types/global.d.ts

@ -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 { }

118
src/uni.scss

@ -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;
}
}
}

58
src/utils/common.ts

@ -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
}

61
src/utils/http.ts

@ -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
}
})
})
}
}

6
src/utils/rules.ts

@ -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)
}

29
tsconfig.json

@ -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"
]
}

45
vite.config.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), '')
}
}
}
}
})
Loading…
Cancel
Save