Compare commits

...

No commits in common. 'master' and 'dev' have entirely different histories.
master ... dev

  1. 3
      .babelrc
  2. 30
      .env.development
  3. 29
      .env.production
  4. 3
      .eslintignore
  5. 146
      .eslintrc.cjs
  6. 30
      .gitignore
  7. 4
      .husky/pre-commit
  8. 8
      .prettierignore
  9. 36
      .prettierrc.cjs
  10. 30
      README.md
  11. 85
      auto/addPage.ts
  12. 4
      auto/preinstall.js
  13. 17
      index.html
  14. 11537
      package-lock.json
  15. 92
      package.json
  16. 8312
      pnpm-lock.yaml
  17. BIN
      public/niucloud.ico
  18. 51
      publish.cjs
  19. 132
      src/App.vue
  20. 25
      src/api/common.ts
  21. 39
      src/api/login.ts
  22. 61
      src/api/user.ts
  23. 44
      src/api/votingElection.ts
  24. 97
      src/app/api/auth.ts
  25. 39
      src/app/api/device.ts
  26. 29
      src/app/api/diy.ts
  27. 101
      src/app/api/home.ts
  28. 244
      src/app/api/member.ts
  29. 8
      src/app/api/mine.ts
  30. 15
      src/app/api/pay.ts
  31. 76
      src/app/api/shop.ts
  32. 147
      src/app/api/system.ts
  33. 47
      src/app/api/verify.ts
  34. 219
      src/app/components/diy/active-cube/index.vue
  35. 634
      src/app/components/diy/carousel-search/index.vue
  36. 113
      src/app/components/diy/float-btn/index.vue
  37. 257
      src/app/components/diy/graphic-nav/index.vue
  38. 44
      src/app/components/diy/horz-blank/index.vue
  39. 40
      src/app/components/diy/horz-line/index.vue
  40. 91
      src/app/components/diy/hot-area/index.vue
  41. 140
      src/app/components/diy/image-ads/index.vue
  42. 185
      src/app/components/diy/member-info/index.vue
  43. 220
      src/app/components/diy/member-level/index.vue
  44. 291
      src/app/components/diy/notice/index.vue
  45. 125
      src/app/components/diy/rich-text/index.vue
  46. 590
      src/app/components/diy/rubik-cube/index.vue
  47. 129
      src/app/components/diy/text/index.vue
  48. 15
      src/app/components/fixed/demo-index/index.vue
  49. 37
      src/app/components/tag/tag.vue
  50. 6
      src/app/locale/en/pages.setting.index.json
  51. 5
      src/app/locale/zh-Hans/pages.article.detail.json
  52. 7
      src/app/locale/zh-Hans/pages.article.list.json
  53. 10
      src/app/locale/zh-Hans/pages.auth.bind.json
  54. 14
      src/app/locale/zh-Hans/pages.auth.login.json
  55. 16
      src/app/locale/zh-Hans/pages.auth.register.json
  56. 6
      src/app/locale/zh-Hans/pages.auth.resetpwd.json
  57. 10
      src/app/locale/zh-Hans/pages.index.develop.json
  58. 7
      src/app/locale/zh-Hans/pages.member.account.json
  59. 17
      src/app/locale/zh-Hans/pages.member.account_edit.json
  60. 6
      src/app/locale/zh-Hans/pages.member.address.json
  61. 12
      src/app/locale/zh-Hans/pages.member.address_edit.json
  62. 30
      src/app/locale/zh-Hans/pages.member.apply_cash_out.json
  63. 15
      src/app/locale/zh-Hans/pages.member.balance.json
  64. 11
      src/app/locale/zh-Hans/pages.member.cash_out.json
  65. 12
      src/app/locale/zh-Hans/pages.member.cash_out_detail.json
  66. 15
      src/app/locale/zh-Hans/pages.member.commission.json
  67. 7
      src/app/locale/zh-Hans/pages.member.detailed_account.json
  68. 14
      src/app/locale/zh-Hans/pages.member.location_address_edit.json
  69. 12
      src/app/locale/zh-Hans/pages.member.personal.json
  70. 4
      src/app/locale/zh-Hans/pages.member.recharge_record.json
  71. 4
      src/app/locale/zh-Hans/pages.member.recharge_record_detail.json
  72. 13
      src/app/locale/zh-Hans/pages.member.withdrawal_detail.json
  73. 6
      src/app/locale/zh-Hans/pages.setting.index.json
  74. 25
      src/app/pages/auth/agreement.vue
  75. 205
      src/app/pages/auth/bind.vue
  76. 184
      src/app/pages/auth/login.vue
  77. 229
      src/app/pages/auth/register.vue
  78. 135
      src/app/pages/auth/resetpwd.vue
  79. BIN
      src/app/pages/commodity/commodity.png
  80. 156
      src/app/pages/commodity/index.vue
  81. 14
      src/app/pages/electionResults/index.vue
  82. 12
      src/app/pages/mine/index.vue
  83. 14
      src/app/pages/myElection/index.vue
  84. 368
      src/app/pages/votingElection/index.vue
  85. 33
      src/app/pages/webview/index.vue
  86. 138
      src/app/stores/diy.ts
  87. 56
      src/components/PullToRefresh.vue
  88. 154
      src/components/area-select/area-select.vue
  89. 117
      src/components/information-filling/information-filling.vue
  90. 22
      src/components/libs/config/index.ts
  91. 145
      src/components/libs/hooks/useListLoadClass/index.ts
  92. 294
      src/components/libs/util/index.ts
  93. 133
      src/components/liu-checkBox/liu-checkBox.vue
  94. 249
      src/components/lx-header/lx-header.vue
  95. 35
      src/components/lx-img/lx-img.vue
  96. 92
      src/components/lx-list-state/lx-list-state.vue
  97. 102
      src/components/lx-list/lx-list.vue
  98. 66
      src/components/mescroll/hooks/useMescroll.js
  99. 56
      src/components/mescroll/hooks/useMescrollComp.js
  100. 69
      src/components/mescroll/hooks/useMescrollMore.js

3
.babelrc

@ -1,3 +0,0 @@
{
"presets": ["@babel/preset-env"]
}

30
.env.development

@ -1,25 +1,5 @@
NODE_ENV = 'development'
# api请求地址
VITE_APP_BASE_URL='http://shop.zeyan.wang/index.php/api/'
# 图片服务器地址
VITE_IMG_DOMAIN='http://shop.zeyan.wang/'
# 站点id 仅在编译为小程序时生效
VITE_SITE_ID = '100000'
# 本地存储时token的参数名
VITE_REQUEST_STORAGE_TOKEN_KEY='wapToken'
# 请求时header中token的参数名
VITE_REQUEST_HEADER_TOKEN_KEY='token'
# 请求时header中站点的参数名
VITE_REQUEST_HEADER_SITEID_KEY='site-id'
# 请求时header中来源场景的参数名
VITE_REQUEST_HEADER_CHANNEL_KEY='channel'
# 应用版本
VITE_APP_VERSION='1.0.1'
ENV='development'
# base api
VITE_APP_BASE_URL = 'https://evote.truescloud.com'
VITE_APP_BASE_PRE = '/dev-api'
VITE_APP_BASE_NAME = 'POS'

29
.env.production

@ -1,25 +1,6 @@
NODE_ENV = 'production'
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'
# api请求地址
VITE_APP_BASE_URL='http://shop.zeyan.wang/index.php/api/'
# 图片服务器地址
VITE_IMG_DOMAIN='http://shop.zeyan.wang/'
# 站点id 仅在编译为小程序时生效
VITE_SITE_ID = '100000'
# 本地存储时token的参数名
VITE_REQUEST_STORAGE_TOKEN_KEY='wapToken'
# 请求时header中token的参数名
VITE_REQUEST_HEADER_TOKEN_KEY='token'
# 请求时header中站点的参数名
VITE_REQUEST_HEADER_SITEID_KEY='site-id'
# 请求时header中来源场景的参数名
VITE_REQUEST_HEADER_CHANNEL_KEY='channel'
# 应用版本
VITE_APP_VERSION='1.0.1'

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

@ -1,22 +1,30 @@
# Logs
logs
*.log
.DS_Store
/node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
*.local
# Editor directories and files
.project
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.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'
}

30
README.md

@ -1,2 +1,30 @@
# Election
## 项目介绍
[uniapp-vue-vite-ts](https://gitee.com/lxxlalala/uniapp-vue-vite-ts) 是基于 Vue3 + Vite4+ TypeScript + uview-plus V3 + Pinia 等最新主流技术栈构建的uniapp前端模板。
| 环境 | 名称版本 |
| -------------------- | :----------------------------------------------------------- |
| **开发工具** | VSCode |
| **运行环境** | Node 16+ |
| **VSCode插件(必装)** | 1. `Vue Language Features (Volar) ` <br/>2. `TypeScript Vue Plugin (Volar) ` <br/>3. 禁用 Vetur <br/>4.ESLint <br/>5.Prettier - Code formatter |
## 项目启动
```bash
# 安装 pnpm
npm install pnpm -g
# 安装依赖
pnpm install
# 启动运行
pnpm run dev:h5 (或者查看package.json运行对应平台命令)
```
## 项目文档
- [ESLint+Prettier+Husky 约束和统一前端代码规范](https://blog.csdn.net/qq_51091386/article/details/132099829)

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

17
index.html

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<html>
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
@ -12,9 +13,11 @@
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.js"></script>
</body>
</head>
<body>
<view id="app"><!--app-html--></view>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

11537
package-lock.json

File diff suppressed because it is too large

92
package.json

@ -7,6 +7,7 @@
"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",
@ -23,7 +24,7 @@
"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 && node publish.cjs",
"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",
@ -36,51 +37,64 @@
"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"
"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-3080720230703001",
"@dcloudio/uni-app-plus": "3.0.0-3080720230703001",
"@dcloudio/uni-components": "3.0.0-3080720230703001",
"@dcloudio/uni-h5": "3.0.0-3080720230703001",
"@dcloudio/uni-mp-alipay": "3.0.0-3080720230703001",
"@dcloudio/uni-mp-baidu": "3.0.0-3080720230703001",
"@dcloudio/uni-mp-jd": "3.0.0-3080720230703001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-3080720230703001",
"@dcloudio/uni-mp-lark": "3.0.0-3080720230703001",
"@dcloudio/uni-mp-qq": "3.0.0-3080720230703001",
"@dcloudio/uni-mp-toutiao": "3.0.0-3080720230703001",
"@dcloudio/uni-mp-weixin": "3.0.0-3080720230703001",
"@dcloudio/uni-quickapp-webview": "3.0.0-3080720230703001",
"feng-uniapp-exploit": "^1.0.2",
"html2canvas": "^1.4.1",
"image-tools": "^1.4.0",
"lodash-es": "^4.17.21",
"@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",
"qrcode": "^1.5.1",
"qs": "6.7.0",
"sortablejs": "^1.15.0",
"uview-plus": "^3.4.6",
"vue": "^3.3.0",
"vue-i18n": "^9.2.2",
"weixin-js-sdk": "^1.6.0"
"sass": "^1.63.2",
"uview-plus": "^3.4.9",
"vue": "^3.2.45",
"vue-i18n": "^9.1.9"
},
"devDependencies": {
"@dcasia/mini-program-tailwind-webpack-plugin": "^1.5.6",
"@babel/eslint-parser": "^7.22.9",
"@dcloudio/types": "^3.3.2",
"@dcloudio/uni-automator": "3.0.0-3080720230703001",
"@dcloudio/uni-cli-shared": "3.0.0-3080720230703001",
"@dcloudio/uni-stacktracey": "3.0.0-3080720230703001",
"@dcloudio/vite-plugin-uni": "3.0.0-3080720230703001",
"@rollup/plugin-commonjs": "^24.0.1",
"@types/qrcode": "^1.5.0",
"@types/sortablejs": "^1.15.0",
"@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",
"sass": "^1.54.5",
"@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",
"vite": "4.0.4",
"vite-plugin-windicss": "^1.8.10",
"vue-tsc": "^1.0.24",
"windicss": "^3.5.6"
"unplugin-auto-import": "^0.16.6",
"unplugin-vue-components": "^0.25.1",
"vite": "4.0.3",
"vue-tsc": "^1.0.24"
}
}

8312
pnpm-lock.yaml

File diff suppressed because it is too large

BIN
public/niucloud.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

51
publish.cjs

@ -1,51 +0,0 @@
const fs = require('fs')
const publish = () => {
const src = './dist/build/h5'
const dest = '../niucloud/public/wap'
solve()
// 目标目录不存在停止复制
try {
const dir = fs.readdirSync(dest)
} catch (e) {
return
}
// 删除目标目录下文件
fs.rm(dest, { recursive: true }, err => {
if(err) {
console.log(err)
return
}
fs.cp(src, dest, { recursive: true }, (err) => {
if (err) {
console.error(err)
}
})
})
}
const solve = () => {
const src = './dist/build/h5/assets'
const filemaps = fs.readdirSync(src)
filemaps.forEach(file => {
if (/^(index-)(\w{8})(.js)$/.test(file)) {
const path = `${src}/${file}`
let content = fs.readFileSync(path, 'utf-8')
const first = 'const match = location.href.match(/\\/wap\\/(\\d*)\\//);'
if (content.indexOf(first) == -1) {
content = first + content
const replace = 'router:{mode:"history",base: match ? `/wap/${match[1]}/` : "/wap/",assets:"assets",routerBase: match ? `/wap/${match[1]}/` : "/wap/"},darkmode'
content = content.replace(/router:{(.*?)},darkmode/s, replace)
fs.writeFileSync(path, content, 'utf8')
}
}
})
}
publish()

132
src/App.vue

@ -1,111 +1,29 @@
<template>
<view id="app">
<router-view></router-view>
</view>
</template>
<script setup lang="ts">
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
import { launchInterceptor } from '@/utils/interceptor'
import { getToken, isWeixinBrowser, getSiteId } from '@/utils/common'
import useMemberStore from '@/stores/member'
import useConfigStore from '@/stores/config'
import useSystemStore from '@/stores/system'
import { useLogin } from '@/hooks/useLogin'
import { useShare } from '@/hooks/useShare'
onLaunch(async(data) => {
// //
// launchInterceptor()
// // #ifdef H5
// uni.getSystemInfoSync().platform == 'ios' && (uni.setStorageSync('initUrl', location.href))
// //
// window.parent.postMessage(JSON.stringify({
// type: 'appOnLaunch',
// message: ''
// }), '*');
// //
// window.addEventListener('message', event => {
// try {
// let data = {
// type: ''
// };
// if (typeof event.data == 'string') {
// data = JSON.parse(event.data)
// } else if (typeof event.data == 'object') {
// data = event.data
// }
// if (data.type && data.type == 'appOnReady') {
// window.parent.postMessage(JSON.stringify({
// type: 'appOnReady',
// message: ''
// }), '*');
// }
// } catch (e) {
// console.log('uni-app App.vue ', e)
// }
// }, false);
// // id
// if (process.env.NODE_ENV == 'development' && (getSiteId(uni.getStorageSync('wap_site_id') || import.meta.env.VITE_SITE_ID) === '')) return;
// const { wechatInit } = useShare()
// wechatInit()
// // #endif
// const configStore = useConfigStore()
// await configStore.getTabbarConfig()
// await configStore.getLoginConfig()
// useSystemStore().getMapFn()
// useSystemStore().getSiteInfoFn()
// // try {
// // // tabbar
// // uni.hideTabBar()
// // } catch (e) {
// // }
// //
// if (getToken()) {
// const memberStore = useMemberStore()
// await memberStore.setToken(getToken())
// setTimeout(() => {
// if (!uni.getStorageSync('openid')) {
// const memberInfo = useMemberStore().info
// // #ifdef MP-WEIXIN
// memberInfo && memberInfo.weapp_openid && uni.setStorageSync('openid', memberInfo.weapp_openid)
// // #endif
// // #ifdef H5
// isWeixinBrowser() && memberInfo && memberInfo.wx_openid && uni.setStorageSync('openid', memberInfo.wx_openid)
// // #endif
// }
// }, 1000)
// }
// if (!getToken()) {
// const login = useLogin()
// //
// // #ifdef MP
// login.getAuthCode()
// // #endif
// // #ifdef H5
// if (isWeixinBrowser()) {
// data.query.code ? login.authLogin(data.query.code) : login.getAuthCode('snsapi_userinfo')
// }
// // #endif
// }
})
onShow(() => {
})
onHide(() => {
})
// import routingIntercept from '@/permission'
onLaunch(() => {
// routingIntercept()
})
onShow(() => {
console.log('App Show')
})
onHide(() => {
console.log('App Hide')
})
//
// provide('globalObj', <globalObjInt>{
// //
// goToPage
// });
// //
</script>
<style>
uni-page-head {
display: none !important;
}
<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: any) {
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
})
}

97
src/app/api/auth.ts

@ -1,97 +0,0 @@
import request from '@/utils/request'
/**
*
*/
export function usernameLogin(data : AnyObject) {
return request.get('login', data, { showErrorMessage: true })
}
/**
*
*/
export function mobileLogin(data : AnyObject) {
return request.post('login/mobile', data, { showErrorMessage: true })
}
/**
*
*/
export function getConfig() {
return request.get('login/config')
}
/**
* 退
*/
export function logout() {
return request.put('auth/logout')
}
/**
*
*/
export function usernameRegister(data : AnyObject) {
let url = 'register'
if(uni.getStorageSync('pid')){
data.pid = uni.getStorageSync('pid');
}
return request.post(url, data, { showErrorMessage: true })
}
/**
*
*/
export function mobileRegister(data : AnyObject) {
let url = 'register/mobile'
if(uni.getStorageSync('pid')){
data.pid = uni.getStorageSync('pid');
}
return request.post(url, data, { showErrorMessage: true })
}
/**
*
*/
export function wechatUser(data : AnyObject) {
return request.get('wechat/user', data, { showErrorMessage: false })
}
/**
* openid
*/
export function wechatUserLogin(data : AnyObject) {
return request.post('wechat/userlogin', data, { showErrorMessage: true })
}
/**
*
*/
export function wechatLogin(data : AnyObject) {
return request.post('wechat/login', data, { showErrorMessage: false })
}
/**
*
*/
export function weappLogin(data : AnyObject) {
return request.post('weapp/login', data, { showErrorMessage: false })
}
/**
*
*/
export function bind(data : AnyObject) {
let url = 'bind'
if(uni.getStorageSync('pid')){
data.pid = uni.getStorageSync('pid');
}
return request.post(url, data, { showErrorMessage: true })
}
/**
* 访
*/
export function memberLog(data : AnyObject) {
return request.post('member/log', data, { showErrorMessage: false })
}

39
src/app/api/device.ts

@ -1,39 +0,0 @@
import request from '@/utils/request'
/**
*
*/
export function set_equipment(params: AnyObject) {
return request.get(`member/set_equipment`, params)
}
/**
*
*/
export function list_archives() {
return request.get(`member/list_archives`)
}
/**
*
*/
export function edit_archives(data : any) {
return request.post(`member/edit_archives`, data)
}
/**
*
*/
export function del_archives(id : number) {
return request.get(`member/del_archives?id=`+ id)
}
/**
*
*/
export function list_equipment() {
return request.get(`member/list_equipment`)
}
/**
*
*/
export function del_equipment(id : number) {
return request.get(`member/del_equipment?id=`+ id)
}

29
src/app/api/diy.ts

@ -1,29 +0,0 @@
import request from '@/utils/request'
/**
*
*/
export function getDiyInfo(params: Record<string, any>) {
return request.get('diy/diy', params)
}
/**
*
*/
export function getTabbarInfo(params: Record<string, any>) {
return request.get('diy/tabbar', params)
}
/**
*
*/
export function getTabbarList(params: Record<string, any>) {
return request.get('diy/tabbar/list', params)
}
/**
*
*/
export function getShareInfo(params: Record<string, any>) {
return request.get('diy/share', params)
}

101
src/app/api/home.ts

@ -1,101 +0,0 @@
import request from "@/utils/request";
// 获取轮播图
export function getCarousel() {
return request.get('/shop/slideshow/list')
}
// 获取分类
export function getClassification() {
return request.get('/shop/homeCategory/list')
}
// 获取文章分类
export function getArticle(data) {
return request.get('/cms/category',data)
}
// 获取医生
export function getDoctor(data) {
return request.get('/shop/doctor/list',data)
}
// 获取驿站
export function getDak(data) {
return request.get('/shop/communityStation/list',data)
}
// 获取科室分类
export function getDepartmentClassification(data) {
return request.get('/shop/doctorsDepartment/list',data)
}
// 获取驿站详情
export function getRelayDetails(data) {
return request.get('/shop/communityStation/info',data)
}
// 获取行政区划
export function getAdministrativeDivision() {
return request.get('/accompany/getAreaTree')
}
// 获取专题列表
export function getTopicsList(data) {
return request.get('/cms/article',data)
}
// 获取专题详情
export function getTopicsDetails(id) {
return request.get(`/cms/article/${id}`)
}
// 获取医院分类
export function getHospitalClassification() {
return request.get(`/shop/doctor/getHospitalsAll`)
}
// 获取医生详情
export function getDoctorDetails(data) {
return request.get(`/shop/goods/detail`,data)
}
// 商品详情
export function getProductDetails(data) {
return request.get(`/shop/goods/detail`,data)
}
// 订单计算
export function orderCalculation(data) {
return request.get(`/shop/order_create/calculate`,data)
}
// 订单创建
export function orderCreation(data) {
return request.post(`/shop/order_create/create`,data)
}
// 获取通知
export function getNotifications(data) {
return request.get(`/shop/memberNotifications/lists`,data )
}
// 获取用户信息
export function getUserInfo() {
return request.get(`/member/member`)
}
// 获取客服
export function customerService() {
return request.get(`/shop/shopConfig/info`)
}
// 获取支付信息
export function getPaymentInfo(trade_type,trade_id) {
return request.get(`/pay/info/${trade_type}/${trade_id}`)
}
// 去支付
export function toPay(data) {
return request.post(`/pay`,data)
}

244
src/app/api/member.ts

@ -1,244 +0,0 @@
import request from '@/utils/request'
export function getMemberInfo() {
return request.get('member/member')
}
/**
*
*/
export function getPointList(data : AnyObject) {
return request.get('member/account/point', data)
}
/**
*
*/
export function getBalanceList(data : AnyObject) {
return request.get('member/account/balance', data)
}
/**
* ,
*/
export function getBalanceListAll(data : AnyObject) {
return request.get('member/account/balance_list', data)
}
/**
*
*/
export function getMoneyList(data : AnyObject) {
return request.get('member/account/money', data)
}
/**
*
*/
export function modifyMember(data : AnyObject) {
return request.put(`member/modify/${data.field}`, data, { showErrorMessage: true })
}
/**
*
*/
export function bindMobile(data : AnyObject) {
return request.put('member/mobile', data, { showErrorMessage: true })
}
/**
*
*/
export function cashOutTransferType() {
return request.get('member/cash_out/transfertype')
}
/**
*
*/
export function cashOutConfig() {
return request.get('member/cash_out/config')
}
/**
*
*/
export function cashOutApply(data : AnyObject) {
return request.post('member/cash_out/apply', data, { showSuccessMessage: true, showErrorMessage: true })
}
/**
*
*/
export function getCashoutAccountInfo(data : AnyObject) {
return request.get(`member/cashout_account/${data.account_id}`, {})
}
/**
*
*/
export function getFirstCashoutAccountInfo(data : AnyObject) {
return request.get('member/cashout_account/firstinfo', data)
}
/**
*
*/
export function getCashoutAccountList(data : AnyObject) {
return request.get(`member/cashout_account`, data)
}
/**
*
*/
export function getCashOutList(data : AnyObject) {
return request.get(`member/cash_out`, data)
}
/**
*
*/
export function getCashOutDetail(id : number) {
return request.get(`member/cash_out/${id}`)
}
/**
*
*/
export function addCashoutAccount(data : AnyObject) {
return request.post('member/cashout_account', data, { showSuccessMessage: true, showErrorMessage: true })
}
/**
*
*/
export function editCashoutAccount(data : AnyObject) {
return request.put(`member/cashout_account/${data.account_id}`, data, { showSuccessMessage: true, showErrorMessage: true })
}
/**
*
*/
export function deleteCashoutAccount(accountId: number) {
return request.delete(`member/cashout_account/${accountId}`, { showSuccessMessage: true, showErrorMessage: true })
}
/**
*
*/
export function getMemberCommission(data : AnyObject) {
return request.get(`member/account/commission`,data)
}
/**
*
*/
export function getCommissionList(data : AnyObject) {
return request.get(`member/account/commission`, data)
}
/**
*
*/
export function getAccountType(params: Record<string, any>) {
return request.get(`member/account/fromtype/${params.account_type}`)
}
/**
*
* @param params
* @returns
*/
export function getAddressList(params: Record<string, any>) {
return request.get(`member/address`, params)
}
/**
*
* @param id id
* @returns
*/
export function getAddressInfo(id: number) {
return request.get(`member/address/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addAddress(params: Record<string, any>) {
return request.post('member/address', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editAddress(params: Record<string, any>) {
return request.put(`member/address/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteAddress(id: number) {
return request.delete(`member/address/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
*/
export function getMemberLevel() {
return request.get(`member/level`);
}
/**
*
*/
export function getTaskGrowth() {
return request.get(`task/growth`);
}
/**
*
*/
export function getSignInfo(data : AnyObject) {
return request.get(`member/sign/info/${data.year}/${data.month}`, {})
}
/**
*
*/
export function getDayPack(data : AnyObject) {
return request.get(`member/sign/award/${data.year}/${data.month}/${data.day}`)
}
/**
*
*/
export function getSignConfig() {
return request.get(`member/sign/config`)
}
/**
*
* @returns
*/
export function setSign() {
return request.post('member/sign')
}
/**
*
*/
export function getMemberAccountPointcount() {
return request.get(`member/account/pointcount`)
}
/**
*
*/
export function getTaskPoint() {
return request.get(`task/point`)
}

8
src/app/api/mine.ts

@ -1,8 +0,0 @@
import request from '@/utils/request'
/**
*
*/
export function getUserDet() {
return request.get('member/member')
}

15
src/app/api/pay.ts

@ -1,15 +0,0 @@
import request from '@/utils/request'
/**
*
*/
export function pay(data : AnyObject) {
return request.post(`pay`, data, { showErrorMessage: true })
}
/**
*
*/
export function getPayInfo(tradeType : string, tradeId : number) {
return request.get(`pay/info/${tradeType}/${tradeId}`, {}, { showErrorMessage: true })
}

76
src/app/api/shop.ts

@ -1,76 +0,0 @@
import request from '@/utils/request'
/**
*
*/
export function goodsTree() {
return request.get(`shop/goods/category/tree`)
}
/**
*
*/
export function goodsList(params : Record<string, any>) {
return request.get(`shop/goods/pages`, params)
}
/**
*
*/
export function goodsDetail(params : Record<string, any>) {
return request.get(`shop/goods/detail`, params)
}
/**
* -
*/
export function goodsCollect(id : number) {
return request.post(`shop/goods/collect/`+id)
}
/**
* -
*/
export function shopCart(data : any) {
return request.post(`shop/cart`, data)
}
/**
* -
*/
export function shopcartList() {
return request.get(`shop/cart`)
}
/**
* -
*/
export function deleteCart(data : any) {
return request.put(`shop/cart/delete`, data)
}
/**
*
*/
export function categoryList(params : Record<string, any>) {
return request.get(`shop/goods/category/list`, params)
}
/**
* -(1)
*/
export function calculateCreate(params : Record<string, any>) {
return request.get(`shop/order_create/calculate`, params)
}
/**
* -(2-)
*/
export function orderCreate(data : Record<string, any>) {
return request.post(`shop/order_create/create`, data)
}
/**
*
*/
export function getPayInfo(tradeType : string, tradeId : number) {
return request.get(`pay/info/${tradeType}/${tradeId}`, {}, { showErrorMessage: true })
}
/**
* (2)
*/
export function goPayy(data : Record<string, any>) {
return request.post(`pay`,data)
}

147
src/app/api/system.ts

@ -1,147 +0,0 @@
import request from '@/utils/request'
/**
*
*/
export function getCaptcha() {
return request.get('captcha', {}, {showErrorMessage: true})
}
/**
*
*/
export function getWechatAuthCode(data: AnyObject) {
return request.get('wechat/codeurl', data, {showErrorMessage: false})
}
/**
*
*/
export function wechatSync(data: AnyObject) {
return request.post('wechat/sync', data, {showErrorMessage: false})
}
/**
*
*/
export function getAgreementInfo(key: string) {
return request.get(`agreement/${key}`)
}
/**
*
*/
export function resetPassword(data: AnyObject) {
return request.post(`password/reset`, data, {showErrorMessage: true})
}
/**
*
*/
export function sendSms(data: AnyObject) {
return request.post(`send/mobile/${data.type}`, data, {showErrorMessage: true})
}
/**
* jssdk config
*/
export function getWechatSkdConfig(data: AnyObject) {
return request.get('wechat/jssdkconfig', data, {showErrorMessage: false})
}
/**
*
*/
export function uploadImage(data: AnyObject) {
return request.upload('file/image', data, {showErrorMessage: true})
}
/**
*
*/
export function fetchImage(data: AnyObject) {
return request.post('file/image/fetch', data)
}
/**
* base64图片
*/
export function fetchBase64Image(data: AnyObject) {
return request.post('file/image/base64', data)
}
/**
*
*/
export function getSiteInfo() {
return request.get('site')
}
/**
* id
*/
export function getWeappTemplateId(keys: string) {
return request.get('weapp/subscribemsg', {keys})
}
/**
*
* @param pid
*/
export function getAreaListByPid(pid: number = 0) {
return request.get(`area/list_by_pid/${pid}`)
}
/**
*
* @param level
*/
export function getAreatree(level: number = 1) {
return request.get(`area/tree/${level}`)
}
/**
*
* @param code
*/
export function getAreaByCode(code: number | string) {
return request.get(`area/code/${code}`)
}
/**
*
* @param params
*/
export function getAddressByLatlng(params: Record<string, any>) {
return request.get(`area/address_by_latlng`, params, {showErrorMessage: true})
}
/**
*
*/
export function getWapIndexList(data: AnyObject) {
return request.get('wap_index', data)
}
/**
*
* @returns
*/
export function getPoster(params: Record<string, any>) {
return request.get("poster", params)
}
/**
*
*/
export function getMap() {
return request.get('map')
}
/**
*
* @param params
*/
export function getMsgJumpPath(params: Record<string, any>) {
return request.get('weapp/getMsgJumpPath', params)
}

47
src/app/api/verify.ts

@ -1,47 +0,0 @@
import request from '@/utils/request'
/**
*
*/
export function getVerifyCode(type: string ,params: AnyObject) {
return request.get('verify', {type, data: params})
}
/**
*
*/
export function getVerifyRecords(params: Record<string, any>) {
return request.get('verify_records', params)
}
/**
*
*/
export function getCheckVerifier() {
return request.get('check_verifier')
}
/**
*
*/
export function getVerifierInfo(code: string) {
return request.get(`get_verify_by_code/${code}`)
}
/**
*
*/
export function verify(code: string) {
return request.post(`verify/${code}`,{}, { showSuccessMessage: true, showErrorMessage: true })
}
/**
*
*/
export function getVerifyDetail(code: string) {
return request.get(`verify_detail/${code}`,{}, { showErrorMessage: true })
}

219
src/app/components/diy/active-cube/index.vue

@ -1,219 +0,0 @@
<template>
<view :style="warpCss">
<view :style="maskLayer"></view>
<view class="diy-active-cube relative">
<view class="active-cube-wrap p-[20rpx]">
<view class="flex items-center" v-if="diyComponent.titleStyle.value == 'style-1'">
<view class="mr-[20rpx] font-bold text-[32rpx]" :style="{color: diyComponent.titleColor }" @click="diyStore.toRedirect(diyComponent.textLink)">{{ diyComponent.text }}</view>
<view v-if="diyComponent.subTitle.text" @click="diyStore.toRedirect(diyComponent.subTitle.link)" class="text-center text-[24rpx] rounded-[40rpx] rounded-tl-none py-[10rpx] px-[20rpx]" :style="{'color': diyComponent.subTitle.textColor, background: 'linear-gradient(90deg, '+ diyComponent.subTitle.startColor + ', '+ diyComponent.subTitle.endColor + ')'}">{{ diyComponent.subTitle.text }}</view>
</view>
<view class="flex items-center" v-if="diyComponent.titleStyle.value == 'style-2'">
<view class="mr-[20rpx] font-bold text-[32rpx]" :style="{color: diyComponent.titleColor }" @click="diyStore.toRedirect(diyComponent.textLink)">{{ diyComponent.text }}</view>
<view v-if="diyComponent.subTitle.text" @click="diyStore.toRedirect(diyComponent.subTitle.link)" class="text-center text-[24rpx] rounded-[10rpx] py-[10rpx] px-[20rpx]" :style="{'color': diyComponent.subTitle.textColor, background: 'linear-gradient(90deg, '+ diyComponent.subTitle.startColor + ', '+ diyComponent.subTitle.endColor + ')'}">{{ diyComponent.subTitle.text }}</view>
</view>
<view class="flex items-center" v-if="diyComponent.titleStyle.value == 'style-3'">
<view class="mr-[20rpx] font-bold text-[32rpx]" @click="diyStore.toRedirect(diyComponent.textLink)" :style="{color: diyComponent.titleColor }">{{ diyComponent.text }}</view>
<view class="relative h-[44rpx]" @click="diyStore.toRedirect(diyComponent.subTitle.link)">
<view v-if="diyComponent.subTitle.text" class="text-center text-[24rpx] py-[10rpx] pl-[16rpx] pr-[36rpx]" :style="{'color': diyComponent.subTitle.textColor, background: 'linear-gradient(90deg, '+ diyComponent.subTitle.startColor + ', '+ diyComponent.subTitle.endColor + ')'}">{{ diyComponent.subTitle.text }}</view>
<image class="absolute left-0 top-0 bottom-0 !w-[16rpx] !h-[44rpx]" :src="img('static/resource/images/diy/active_cube/block_style2_1.png')" mode="scaleToFill"/>
<image class="absolute right-0 top-0 bottom-0 !w-[28rpx] !h-[44rpx]" :src="img('static/resource/images/diy/active_cube/block_style2_2.png')" mode="scaleToFill"/>
</view>
</view>
<view class="flex items-center justify-between" v-if="diyComponent.titleStyle.value == 'style-4'">
<view class="font-bold text-[32rpx]" @click="diyStore.toRedirect(diyComponent.textLink)" :style="{color: diyComponent.titleColor }">{{ diyComponent.text }}</view>
<view v-if="diyComponent.subTitle.text" @click="diyStore.toRedirect(diyComponent.subTitle.link)" class="text-[24rpx] rounded-[40rpx] py-[10rpx] pl-[16rpx] pr-[12rpx] flex items-center" :style="{'color': diyComponent.subTitle.textColor, background: 'linear-gradient(90deg, '+ diyComponent.subTitle.startColor + ', '+ diyComponent.subTitle.endColor + ')'}">
<text>{{ diyComponent.subTitle.text }}</text>
<text class="nc-iconfont nc-icon-youV6xx !text-[26rpx]"></text>
</view>
</view>
<view class="bd flex flex-wrap justify-between">
<template v-for="item in diyComponent.list" :key="item.id">
<view v-if="diyComponent.blockStyle.value == 'style-1'" @click="diyStore.toRedirect(item.link)" class="item flex justify-between p-[20rpx] bg-white mt-[20rpx] rounded-[16rpx]" :style="{ backgroundColor : diyComponent.elementBgColor }">
<view class="flex-1 flex items-baseline flex-col">
<view class="text--[28rpx] pb-[20rpx]" :style="{ fontWeight : diyComponent.blockStyle.fontWeight }">{{ item.title.text }}</view>
<view class="text--[24rpx] text-gray-500 pb-[20rpx]">{{ item.subTitle.text }}</view>
<view class="link relative text-[24rpx] leading-[40rpx] flex items-center text-white rounded-r-[20rpx] h-[40rpx] pl-[26rpx] pr-[10rpx]" :style="btnCss(item.moreTitle)" v-if="item.moreTitle.text">
<text class="mr-[8rpx]">{{ item.moreTitle.text }}</text>
<text class="iconfont iconjiantou-you-cuxiantiao-fill !text-[20rpx] text-[#fff]"></text>
<image class="absolute left-0 top-0 bottom-0 !w-[28rpx]" :src="img('static/resource/images/diy/active_cube/block_style1_1.png')" mode="scaleToFill"/>
</view>
</view>
<view class="img-box ml-[10rpx] w-[130rpx]" v-if="item.imageUrl">
<image :src="img(item.imageUrl)" mode="aspectFit" />
</view>
<view class="img-box ml-[10rpx] flex items-center justify-center w-[130rpx] bg-[#f3f4f6]" v-else>
<u-icon name="photo" color="#999" size="50"></u-icon>
</view>
</view>
<view v-if="diyComponent.blockStyle.value == 'style-2'" @click="diyStore.toRedirect(item.link)" class="item flex justify-between p-[20rpx] bg-white mt-[20rpx] rounded-[16rpx]" :style="{ backgroundColor : diyComponent.elementBgColor }">
<view class="flex-1 flex items-baseline flex-col">
<view class="text--[28rpx] pb-[20rpx]" :style="{ fontWeight : diyComponent.blockStyle.fontWeight }">{{ item.title.text }}</view>
<view class="text--[24rpx] text-gray-500 pb-[20rpx]">{{ item.subTitle.text }}</view>
<view class="link relative text-[24rpx] leading-[40rpx] flex items-center text-white rounded-[20rpx] h-[40rpx] pl-[20rpx] pr-[10rpx]" :style="btnCss(item.moreTitle)" v-if="item.moreTitle.text">
<text class="mr-[8rpx]">{{ item.moreTitle.text }}</text>
<text class="iconfont iconjiantou-you-cuxiantiao-fill !text-[20rpx] text-[#fff]"></text>
</view>
</view>
<view class="img-box ml-[10rpx] w-[130rpx]" v-if="item.imageUrl">
<image :src="img(item.imageUrl)" mode="aspectFit" />
</view>
<view class="img-box ml-[10rpx] flex items-center justify-center w-[130rpx] bg-[#f3f4f6]" v-else>
<u-icon name="photo" color="#999" size="50"></u-icon>
</view>
</view>
</template>
</view>
<scroll-view scroll-x="true" class="whitespace-nowrap" v-if="diyComponent.blockStyle.value == 'style-3'">
<view v-for="(item,index) in diyComponent.list" :key="item.id" class="inline-flex">
<view @click="diyStore.toRedirect(item.link)" class="flex flex-col items-center justify-between p-[10rpx] bg-white mt-[20rpx] w-[157rpx] h-[200rpx] rounded-[10rpx] box-border" :class="{'mr-[14rpx]': (index+1 != diyComponent.list.length)}">
<view class="w-[141rpx] h-[141rpx]" v-if="item.imageUrl">
<image class="w-[141rpx] h-[141rpx]" :src="img(item.imageUrl)" mode="aspectFit" />
</view>
<view class="w-[141rpx] h-[141rpx] relative flex-shrink-0" v-else>
<view class="absolute left-0 top-0 flex items-center justify-center w-[141rpx] h-[141rpx] bg-[#f3f4f6]">
<u-icon name="photo" color="#999" size="50"></u-icon>
</view>
</view>
<view class="my-[10rpx] text-[26rpx]" :style="{ color : item.title.textColor,fontWeight : diyComponent.blockStyle.fontWeight }">{{ item.title.text }}</view>
</view>
</view>
</scroll-view>
<scroll-view scroll-x="true" class="whitespace-nowrap" v-if="diyComponent.blockStyle.value == 'style-4'">
<view v-for="(item,index) in diyComponent.list" :key="item.id" class="inline-flex">
<view @click="diyStore.toRedirect(item.link)" class="flex flex-col items-center justify-between p-[4rpx] bg-[#F93D02] mt-[20rpx] rounded-[20rpx] box-border" :class="{'mr-[14rpx]': index+1 != diyComponent.list.length}" :style="{ background : 'linear-gradient('+ item.listFrame.startColor +','+ item.listFrame.endColor + ')' }">
<view class="w-[149rpx] h-[149rpx] box-border px-[18rpx] pt-[16rpx] pb-[6rpx] bg-[#fff] flex flex-col items-center rounded-[16rpx]">
<view class="w-[112rpx] h-[102rpx]" v-if="item.imageUrl">
<image class="w-[112rpx] h-[102rpx]" :src="img(item.imageUrl)" mode="aspectFit" />
</view>
<view class="w-[112rpx] h-[102rpx] relative flex-shrink-0" v-else>
<view class="absolute left-0 top-0 flex items-center justify-center w-[112rpx] h-[102rpx] bg-[#f3f4f6]">
<u-icon name="photo" color="#999" size="50"></u-icon>
</view>
</view>
<view class="relative -mt-[10rpx] text-[22rpx] bg-[#F3DAC5] text-[#ED6E00] rounded-[16rpx] px-[12rpx] leading-[36rpx]" :style="{ color : item.subTitle.textColor, background : 'linear-gradient(to right,'+ item.subTitle.startColor +','+ item.subTitle.endColor + ')' }">{{ item.subTitle.text }}</view>
</view>
<view class="mt-[10rpx] mb-[6rpx] text-[28rpx] text-[#fff]" :style="{ fontWeight : diyComponent.blockStyle.fontWeight }">{{ item.title.text }}</view>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
//
import { ref,computed, watch, onMounted, nextTick,getCurrentInstance } from 'vue';
import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
//
const maskLayer = computed(()=>{
var style = '';
if(diyComponent.value.componentBgUrl) {
style += 'position:absolute;top:0;width:100%;';
style += `background: rgba(0,0,0,${diyComponent.value.componentBgAlpha / 10});`;
style += `height:${height.value}px;`;
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
}
return style;
});
const btnCss = (item:any) => {
var style = '';
style += `background:linear-gradient(90deg,${item.startColor},${item.endColor});`;
return style;
};
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'ActiveCube') {
refresh();
}
}
)
}
});
const instance = getCurrentInstance();
const height = ref(0)
const refresh = ()=> {
nextTick(() => {
const query = uni.createSelectorQuery().in(instance);
query.select('.diy-active-cube').boundingClientRect((data: any) => {
height.value = data.height;
}).exec();
})
}
</script>
<style lang="scss" scoped>
.active-cube-wrap {
.bd {
.item {
width: calc(46% - 20rpx);
image {
width: 100%;
height: 100%;
}
}
}
}
</style>

634
src/app/components/diy/carousel-search/index.vue

@ -1,634 +0,0 @@
<template>
<view :style="warpCss" class="goods-carousel-search-wrap">
<view class="relative pb-[20rpx]">
<view class="bg-img" :class="{'!-bottom-[200rpx]': diyComponent.bgGradient == true}">
<image v-if="diyComponent.swiper.list && diyComponent.swiper.list[swiperIndex].imageUrl" :src="img(diyComponent.swiper.list[swiperIndex].imageUrl)" mode="scaleToFill" class="w-full h-full" :show-menu-by-longpress="true"/>
<view v-else class="w-full h-full bg-[#ccc]"></view>
<view class="bg-img-box" :style="bgImgBoxStyle"></view>
</view>
<view class="fixed-wrap" :class="[ diyStore.mode != 'decorate' ? diyComponent.positionWay : '' ]" :style="fixedStyle">
<view class="diy-search-wrap relative z-10" @click="diyStore.toRedirect(diyComponent.search.link)" :style="navbarInnerStyle">
<view class="img-wrap" v-if="diyComponent.search.logo">
<image :src="img(diyComponent.search.logo)" mode="aspectFit"/>
</view>
<view class="search-content">
<input type="text" class="uni-input" placeholder-style="color:#fff" placeholder-class="!text-[#fff] text-[24rpx] leading-[68rpx]" :placeholder="isShowSearchPlaceholder ? diyComponent.search.text : ''" disabled="true"/>
<text class="nc-iconfont nc-icon-sousuo-duanV6xx1"></text>
<swiper class="swiper-wrap" :interval="diyComponent.search.hotWord.interval * 1000" autoplay="true" vertical="true" circular="true" v-if="!isShowSearchPlaceholder">
<swiper-item class="swiper-item" v-for="(item) in diyComponent.search.hotWord.list" :key="item.id">
<view class=" leading-[64rpx] text-[24rpx]">{{ item.text }}</view>
</swiper-item>
</swiper>
</view>
</view>
<view class="tab-list-wrap relative z-10" v-if="diyComponent.tab.control">
<scroll-view scroll-x="true" class="scroll-wrap" :scroll-into-view="'a' + currTabIndex">
<view @click="changeData({ source : 'home' },-1)" class="scroll-item" :class="[{ active: currTabIndex == -1 }]">
<view class="name" :style="{'color': getTabColor(currTabIndex == -1)}">首页</view>
<view class="line" :style="{'background-color': getTabColor(currTabIndex == -1)}" v-if="currTabIndex == -1"></view>
</view>
<view v-for="(item, index) in diyComponent.tab.list" class="scroll-item" :class="[{ active: index == currTabIndex }]" @click="changeData(item,index)" :id="'a' + index" :key="index">
<view class="name" :style="{'color': getTabColor(index == currTabIndex)}">{{ item.text }}</view>
<view class="line" :style="{'background-color': getTabColor(index == currTabIndex)}" v-if="index == currTabIndex"></view>
</view>
</scroll-view>
<view v-if="diyComponent.tab.list.length" class="absolute tab-btn nc-iconfont nc-icon-yingyongliebiaoV6xx" @click="tabAllPopup = true"></view>
</view>
<view class="bg-img" v-if="fixedStyleBg">
<image v-if="diyComponent.swiper.list && diyComponent.swiper.list[swiperIndex].imageUrl" :src="img(diyComponent.swiper.list[swiperIndex].imageUrl)" mode="scaleToFill" class="w-full h-full" :show-menu-by-longpress="true"/>
<view v-else class="w-full h-full bg-[#ccc]"></view>
</view>
</view>
<!-- 解决fixed定位后导航栏塌陷的问题 -->
<template v-if="diyStore.mode != 'decorate'">
<view v-if="diyComponent.positionWay == 'fixed'" class="u-navbar-placeholder" :style="{ width: '100%', paddingTop: moduleHeight }"></view>
</template>
<!-- 轮播图 -->
<view class="relative" :class="{'mx-[20rpx]': swiperStyle2}">
<swiper v-if="diyComponent.swiper.control" class="swiper" :style="{ height: imgHeight }" autoplay="true" circular="true" @change="swiperChange"
:class="{
'swiper-left': diyComponent.swiper.indicatorAlign == 'left',
'swiper-right': diyComponent.swiper.indicatorAlign == 'right',
'ns-indicator-dots': diyComponent.swiper.indicatorStyle == 'style-2'
}"
:previous-margin="swiperStyle2 ? 0 : '36rpx'" :next-margin="swiperStyle2 ? 0 : '36rpx'"
:interval="diyComponent.swiper.interval * 1000" :indicator-dots="isShowDots"
:indicator-color="diyComponent.swiper.indicatorColor" :indicator-active-color="diyComponent.swiper.indicatorActiveColor">
<swiper-item class="swiper-item" v-for="(item,index) in diyComponent.swiper.list" :key="item.id" :style="swiperWarpCss">
<view @click="diyStore.toRedirect(item.link)">
<view class="item" :style="{height: imgHeight}">
<image v-if="item.imageUrl" :src="img(item.imageUrl)" mode="scaleToFill" :style="swiperWarpCss" :class="['w-full h-full',{'swiper-animation': swiperIndex != index}]" :show-menu-by-longpress="true"/>
<image v-else :src="img('static/resource/images/diy/figure.png')" :style="swiperWarpCss" mode="scaleToFill" :class="['w-full h-full',{'swiper-animation': swiperIndex != index}]" :show-menu-by-longpress="true"/>
</view>
</view>
</swiper-item>
</swiper>
<!-- #ifdef MP-WEIXIN -->
<view v-if="diyComponent.swiper.list.length > 1" :class="[
'swiper-dot-box',
{ 'straightLine': diyComponent.swiper.indicatorStyle == 'style-2' },
{ 'swiper-left': diyComponent.swiper.indicatorAlign == 'left' },
{ 'swiper-right': diyComponent.swiper.indicatorAlign == 'right' }
]">
<view v-for="(numItem, numIndex) in diyComponent.swiper.list" :key="numIndex" :class="['swiper-dot', { active: numIndex == swiperIndex }]" :style="[numIndex == swiperIndex ? { backgroundColor: diyComponent.swiper.indicatorActiveColor } : { backgroundColor: diyComponent.swiper.indicatorColor }]"></view>
</view>
<!-- #endif -->
</view>
<!-- 分类展开 -->
<u-popup :safeAreaInsetTop="true" :show="tabAllPopup" mode="top" @close="tabAllPopup = false">
<view class="text-sm px-[30rpx] pt-3" :style="{'padding-top':(menuButtonInfo.top+'px')}">全部分类</view>
<view class="flex flex-wrap pl-[30rpx] pt-[30rpx]">
<view @click="changeData({ source : 'home' },-1)" :class="['px-[26rpx] border-[2rpx] border-solid border-transparent h-[60rpx] mr-[30rpx] mb-[30rpx] flex items-center justify-center bg-[#F4F4F4] rounded-[8rpx] text-xs', { 'tab-select-popup': currTabIndex == -1 }]">
首页
</view>
<text @click="changeData(item,index)" v-for="(item, index) in diyComponent.tab.list" :key="index"
:class="['px-[26rpx] border-[2rpx] border-solid border-transparent h-[60rpx] mr-[30rpx] mb-[30rpx] flex items-center justify-center bg-[#F4F4F4] rounded-[8rpx] text-xs', { 'tab-select-popup': index == currTabIndex }]">
{{ item.text }}
</text>
</view>
</u-popup>
</view>
<!-- 展示微页面数据 -->
<template v-if="currentSource == 'diy_page'">
<view class="child-diy-template-wrap bg-index">
<diy-group :data="diyPageData"></diy-group>
</view>
</template>
</view>
</template>
<script setup lang="ts">
//
import { ref, reactive, computed, watch, onMounted, nextTick, getCurrentInstance } from 'vue';
import { img } from '@/utils/common';
import useDiyStore from '@/app/stores/diy';
import diyGroup from '@/addon/components/diy/group/index.vue'
import { getDiyInfo } from '@/app/api/diy';
const instance = getCurrentInstance();
const props = defineProps(['component', 'index', 'pullDownRefreshCount', 'global']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
const moduleHeight:any = ref('')
const setModuleLocation = ()=> {
nextTick(() => {
setTimeout(()=>{
const query = uni.createSelectorQuery().in(instance);
query.select('.fixed-wrap').boundingClientRect((data:any) => {
moduleHeight.value = (data.height || 0) + 'px';
}).exec();
})
})
}
const fixedStyleBg = ref(false);
const fixedStyle = computed(()=>{
if (diyStore.mode == 'decorate') return '';
var style = '';
// #ifdef H5
if(props.global.topStatusBar.isShow && props.global.topStatusBar.style == 'style-4') {
style += 'top:' + diyStore.topTabarHeight + 'px;';
}
// #endif
if(diyComponent.value.positionWay == 'fixed') {
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
if(props.global.topStatusBar.isShow) {
style += 'top:' + diyStore.topTabarHeight + 'px;';
}
// #endif
fixedStyleBg.value = false;
if (diyStore.scrollTop > 20) {
let str = diyComponent.value.fixedBgColor;
let arr = str.split(',');
let num = diyComponent.value.fixedBgColor ? parseInt(arr[arr.length-1]) : 0;
if(!diyComponent.value.fixedBgColor || num == 0 ){
fixedStyleBg.value = true;
}else{
fixedStyleBg.value = false;
style += 'background-color:' + diyComponent.value.fixedBgColor + ';';
}
}
}
return style;
})
const getTabColor = (flag:any)=>{
let color = '';
if(flag){
color = diyComponent.value.tab.selectColor;
if(diyComponent.value.positionWay == 'fixed' && diyStore.scrollTop > 20) {
color = diyComponent.value.tab.fixedSelectColor;
}
}else{
color = diyComponent.value.tab.noColor;
if(diyComponent.value.positionWay == 'fixed' && diyStore.scrollTop > 20) {
color = diyComponent.value.tab.fixedNoColor;
}
}
return color;
}
const isShowSearchPlaceholder = computed(()=> {
let flag = true;
for (let i = 0; i < diyComponent.value.search.hotWord.list.length; i++) {
let item = diyComponent.value.search.hotWord.list[i];
if (item.text) {
flag = false;
break;
}
}
return flag;
})
//
const bgImgBoxStyle = computed(()=>{
var style = '';
let str = props.global.pageStartBgColor ? props.global.pageStartBgColor : 'rgba(255,255,255,1)';
if(str.indexOf('(') > -1) {
let arr = str.split('(')[1].split(')')[0].split(',');
if (diyComponent.value.bgGradient == true) {
style += `background: linear-gradient(rgba(${arr[0]}, ${arr[1]}, ${arr[2]}, 0) 65%, rgba(${arr[0]}, ${arr[1]}, ${arr[2]}, 0.6) 70%, rgba(${arr[0]}, ${arr[1]}, ${arr[2]}, 0.85) 80%, rgba(${arr[0]}, ${arr[1]}, ${arr[2]}, 0.95) 90%, rgb(${arr[0]}, ${arr[1]}, ${arr[2]}, 1) 100%);`;
}
}else{
style += `background: (${str});`;
}
return style;
});
//
const swiperStyle2 = computed(()=>{
var style = false;
style = diyComponent.value.swiper.swiperStyle == 'style-2' ? true : false;
return style;
})
const imgHeight = computed(() => {
return (diyComponent.value.swiper.imageHeight * 2) + 'rpx';
})
const swiperIndex = ref(0);
const swiperChange = e => {
swiperIndex.value = e.detail.current;
};
const swiperWarpCss = computed(() => {
var style = '';
if (diyComponent.value.swiper.topRounded) style += 'border-top-left-radius:' + diyComponent.value.swiper.topRounded * 2 + 'rpx;';
if (diyComponent.value.swiper.topRounded) style += 'border-top-right-radius:' + diyComponent.value.swiper.topRounded * 2 + 'rpx;';
if (diyComponent.value.swiper.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.swiper.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.swiper.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.swiper.bottomRounded * 2 + 'rpx;';
return style;
})
const currTabIndex = ref(-1)
const currentSource = ref('')
const changeData = (item:any,index:any)=> {
if (diyStore.mode == 'decorate') return false;
currentSource.value = item.source;
currTabIndex.value = index;
if(item.source == 'home'){
//
diyStore.topFixedStatus = 'home'
}else if (item.source == 'diy_page') {
//
diyStore.topFixedStatus = 'diy'
getDiyInfoFn(item.diy_id);
}
}
let tabAllPopup = ref(false);
let menuButtonInfo:any = {};
let navbarInnerStyle = ref('')
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'CarouselSearch') {
refresh();
}
}
)
}
// (API)
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
if(diyComponent.value.positionWay == 'fixed') {
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
//
//
//
if(props.global.topStatusBar.isShow == false) {
let rightButtonWidth = menuButtonInfo.width ? menuButtonInfo.width * 2 + 'rpx' : '70rpx';
navbarInnerStyle.value += 'padding-right:calc(' + rightButtonWidth + ' + 30rpx);';
navbarInnerStyle.value += 'padding-top:' + menuButtonInfo.top + 'px;';
}
}
// #endif
});
const refresh = ()=> {
setModuleLocation();
changeData({ source : 'home' },-1)
diyComponent.value.swiper.list.forEach((item : any) => {
if (item.imageUrl == '') {
item.imgWidth = 690;
item.imgHeight = 330;
}
});
}
const diyPageData = reactive({
pageMode: 'diy',
title: '',
global: <any>{},
value: []
})
const getDiyInfoFn = (id:any) => {
if(!id){
diyPageData.pageMode = 'diy';
diyPageData.title = '';
diyPageData.global = {};
diyPageData.value = [];
return;
}
getDiyInfo({
id
}).then((res : any) => {
if (res.data.value) {
let data = res.data;
diyPageData.pageMode = data.mode;
diyPageData.title = data.title;
let sources = JSON.parse(data.value);
diyPageData.global = sources.global;
diyPageData.global.topStatusBar.isShow = false; //
diyPageData.global.bottomTabBarSwitch = false; //
diyPageData.value = sources.value;
diyPageData.value.forEach((item, index) => {
item.pageStyle = '';
if(item.pageStartBgColor) {
if (item.pageStartBgColor && item.pageEndBgColor) item.pageStyle += `background:linear-gradient(${item.pageGradientAngle},${item.pageStartBgColor},${item.pageEndBgColor});`;
else item.pageStyle += 'background-color:' + item.pageStartBgColor + ';';
}
if (item.margin) {
if (item.margin.top > 0) {
item.pageStyle += 'padding-top:' + item.margin.top * 2 + 'rpx' + ';';
}
item.pageStyle += 'padding-bottom:' + item.margin.bottom * 2 + 'rpx' + ';';
item.pageStyle += 'padding-right:' + item.margin.both * 2 + 'rpx' + ';';
item.pageStyle += 'padding-left:' + item.margin.both * 2 + 'rpx' + ';';
}
});
uni.setNavigationBarTitle({
title: diyPageData.title
})
}
});
}
//
let isShowDots = ref(true)
// #ifdef H5
isShowDots.value = true;
// #endif
// #ifdef MP-WEIXIN
isShowDots.value = false;
// #endif
</script>
<style lang="scss" scoped>
.goods-carousel-search-wrap{
.bg-img{
position: absolute;
width: 100%;
top: 0;
bottom: 0;
z-index: 0;
-webkit-filter: blur(0);
filter: blur(0);
overflow: hidden;
uni-image, image{
-webkit-filter: blur(15px);
filter: blur(15px);
-webkit-transform: scale(1.5);
transform: scale(1.5);
}
.bg-img-box{
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
}
}
.fixed-wrap {
&.fixed {
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 991;
transition: background .3s;
}
}
.diy-search-wrap{
display: flex;
position: relative;
align-items: center;
padding:20rpx;
.img-wrap{
display: flex;
align-items: center;
justify-content: center;
width: 140rpx;
height: 60rpx;
margin-right: 20rpx;
image{
width: 100%;
height:100%;
}
}
.search-content {
display: flex;
align-items: center;
padding: 0 40rpx;
border-radius: 50rpx;
background-color: rgba(255,255,255,.2);
flex: 1;
position: relative;
input, .uni-input {
box-sizing: border-box;
display: block;
height: 64rpx;
line-height: 68rpx;
width: 100%;
padding-right: 20rpx;
color: #fff;
background: none;
}
.iconfont {
font-size: 30rpx;
font-weight: bold;
color: #fff;
}
.swiper-wrap{
position: absolute;
width:80%;
height: 64rpx;
line-height: 64rpx;
color:#fff;
}
}
}
.tab-list-wrap {
.scroll-wrap {
left: 0;
right: 0;
z-index: 5;
width: 100%;
white-space: nowrap;
box-sizing: border-box;
padding: 20rpx 80rpx 20rpx 20rpx;
}
.scroll-item {
display: inline-block;
text-align: center;
vertical-align: top;
width: auto;
position: relative;
padding: 0 20rpx;
.name {
font-size: 28rpx;
color: #333;
line-height: 38rpx;
margin-bottom: 10rpx;
}
&.active {
position: relative;
.name {
font-size: 32rpx;
line-height: 38rpx;
font-weight: bold;
}
.line{
position: absolute;
bottom: 0;
width: 34rpx;
height: 4rpx;
border-radius: 29%;
left: 50%;
transform: translateX(-50%);
}
}
}
.tab-btn{
font-size: 34rpx;
/* #ifdef H5 */
top: 22rpx;
right: 20rpx;
line-height: 1;
color: #fff;
&::after{
content: "";
position: absolute;
top: 6rpx;
bottom: -2rpx;
left: -14rpx;
width: 4rpx;
background: linear-gradient( 180deg, #FFFFFF 16%, rgba(255,255,255,0) 92%);
}
/* #endif */
/* #ifdef MP-WEIXIN */
top: 24rpx;
right: 20rpx;
color: #fff;
&::after{
content: "";
position: absolute;
top: 2rpx;
bottom: 0;
left: -16rpx;
width: 4rpx;
background: linear-gradient( 180deg, #FFFFFF 16%, rgba(255,255,255,0) 92%);
}
/* #endif */
}
}
.tab-select-popup{
color: var(--primary-color);
border-color: var(--primary-color);
background-color: var(--primary-color-light);
}
.swiper-animation{
transform: scale(0.94, 0.94);
transition-duration: 0.3s;
transition-timing-function: ease;
}
//
.swiper-right :deep(.uni-swiper-dots-horizontal) {
right: 80rpx;
display: flex;
justify-content: flex-end;
transform: translate(0);
}
.swiper-left :deep(.uni-swiper-dots-horizontal) {
left: 80rpx;
transform: translate(0);
}
.swiper.ns-indicator-dots :deep(.uni-swiper-dot) {
width: 18rpx;
height: 6rpx;
border-radius: 4rpx;
}
.swiper.ns-indicator-dots :deep(.uni-swiper-dot-active) {
width: 36rpx;
}
.swiper-dot-box {
position: absolute;
bottom: 20rpx;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 0 80rpx 8rpx;
box-sizing: border-box;
&.swiper-left {
justify-content: flex-start;
}
&.swiper-right {
justify-content: flex-end;
}
.swiper-dot {
background-color: #b2b2b2;
width: 15rpx;
border-radius: 50%;
height: 15rpx;
margin: 8rpx;
}
&.straightLine {
.swiper-dot {
width: 18rpx;
height: 6rpx;
border-radius: 4rpx;
&.active {
width: 36rpx;
}
}
}
}
</style>

113
src/app/components/diy/float-btn/index.vue

@ -1,113 +0,0 @@
<template>
<view :style="warpCss">
<view :class="['float-btn flex flex-col z-1000 items-center px-[24rpx] fixed', diyComponent.bottomPosition, diyStore.mode == 'decorate' ? 'float-btn-border' : '']" :style="floatBtnWrapCss">
<view v-for="(item,index) in diyComponent.list" :key="index" @click="diyStore.toRedirect(item.link)" :class="{'flex items-center justify-center' : true, 'mb-[20rpx]': diyComponent.list.length != index+1 }" :style="floatBtnItemCss">
<image v-if="item && item.imageUrl" :style="floatBtnItemCss" :src="img(item.imageUrl)" mode="aspectFit"></image>
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="aspectFit" :style="floatBtnItemCss"/>
</view>
</view>
</view>
</template>
<script setup lang="ts">
//
import { computed, watch } from 'vue';
import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
const floatBtnItemCss = computed(() => {
var style = '';
style += 'width:' + diyComponent.value.imageSize * 2 + 'rpx;';
style += 'height:' + diyComponent.value.imageSize * 2 + 'rpx;';
style += 'border-radius:' + diyComponent.value.aroundRadius * 2 + 'rpx;';
return style;
})
const floatBtnWrapCss = computed(() => {
let style = '';
if(diyComponent.value.offset){
if(diyComponent.value.bottomPosition == 'lowerRight' || diyComponent.value.bottomPosition == 'lowerLeft'){
style += 'padding-bottom:'+ diyComponent.value.offset * 2 + 'rpx;';
}else if(diyComponent.value.bottomPosition == 'upperRight' || diyComponent.value.bottomPosition == 'upperLeft'){
style += 'padding-top:'+ diyComponent.value.offset * 2 + 'rpx;';
}
}
return style;
})
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
</script>
<style lang="scss" scoped>
.float-btn{
&.decorate-position.upperRight,&.decorate-position.lowerRight {
align-items: flex-end;
}
&.decorate-position.upperLeft,&.decorate-position.lowerLeft {
align-items: baseline;
}
&.upperLeft {
top: 100rpx;
left: 30rpx;
}
&.upperRight {
top: 100rpx;
right: 30rpx;
}
&.lowerLeft {
bottom: 160rpx;
left: 30rpx;
padding-bottom: constant(safe-area-inset-bottom);
/*兼容 IOS<11.2*/
padding-bottom: env(safe-area-inset-bottom);
/*兼容 IOS>11.2*/
}
&.lowerRight {
bottom: 160rpx;
right: 30rpx;
padding-bottom: constant(safe-area-inset-bottom);
/*兼容 IOS<11.2*/
padding-bottom: env(safe-area-inset-bottom);
/*兼容 IOS>11.2*/
}
}
.z-1000{
z-index: 1000;
}
.float-btn-border{
border: 4rpx dashed var(--primary-color);
}
</style>

257
src/app/components/diy/graphic-nav/index.vue

@ -1,257 +0,0 @@
<template>
<view :style="warpCss">
<view :style="maskLayer"></view>
<view class="diy-graphic-nav relative">
<view v-if="diyComponent.layout == 'vertical'" class="graphic-nav">
<view class="graphic-nav-item" v-for="(item, index) in diyComponent.list" :key="item.id">
<view @click="diyStore.toRedirect(item.link)" :class="['flex items-center justify-between py-3 px-4', index == 0 ? 'border-t-0':'border-t']">
<view class="graphic-img relative flex items-center w-10 h-10 mr-[20rpx]"
v-if="diyComponent.mode != 'text'"
:style="{ width: diyComponent.imageSize * 2 + 'rpx', height: diyComponent.imageSize * 2 + 'rpx' }">
<image v-if="item.imageUrl" :src="img(item.imageUrl)" mode="aspectFill"
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="aspectFill"
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
<text v-if="item.label.control"
class="tag absolute -top-[10rpx] -right-[24rpx] text-white rounded-[24rpx] rounded-bl-none transform scale-80 py-1 px-2 text-xs"
:style="{ color: item.label.textColor, backgroundImage: 'linear-gradient(' + item.label.bgColorStart + ',' + item.label.bgColorEnd + ')' }">
{{ item.label.text }}
</text>
</view>
<text v-if="diyComponent.mode != 'img'" class="graphic-text w-full truncate leading-normal"
:style="{ fontSize: diyComponent.font.size * 2 + 'rpx', fontWeight: diyComponent.font.weight, color: diyComponent.font.color }">
{{ item.title }}
</text>
<u-icon name="arrow-right" color="#999999" size="12"></u-icon>
</view>
</view>
</view>
<swiper v-else-if="diyComponent.layout == 'horizontal' && diyComponent.showStyle == 'pageSlide'"
class="graphic-nav box-border relative" circular :indicator-dots="false"
:style="{ height: swiperHeight }" @change="swiperChange">
<swiper-item class="graphic-nav-wrap flex flex-wrap" v-for="(numItem, numIndex) in Math.ceil(diyComponent.list.length / (diyComponent.pageCount * diyComponent.rowCount))">
<template v-for="(item, index) in diyComponent.list">
<view class="graphic-nav-item" :class="[diyComponent.mode]" :key="item.id" v-if="swiperCondition(index,numItem)" :style="{ width: 100 / diyComponent.rowCount + '%' }">
<view @click="diyStore.toRedirect(item.link)" class="flex flex-col items-center box-border py-2">
<view class="graphic-img relative flex items-center justify-center w-10 h-10"
v-if="diyComponent.mode != 'text'"
:style="{ width: diyComponent.imageSize * 2 + 'rpx', height: diyComponent.imageSize * 2 + 'rpx' }">
<image v-if="item.imageUrl" :src="img(item.imageUrl)" mode="aspectFill"
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="aspectFill"
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
<text
class="tag absolute -top-[10rpx] -right-[24rpx] text-white rounded-[24rpx] rounded-bl-none transform scale-80 py-1 px-2 text-xs"
v-if="item.label.control"
:style="{ color: item.label.textColor, backgroundImage: 'linear-gradient(' + item.label.bgColorStart + ',' + item.label.bgColorEnd + ')' }">
{{ item.label.text }}
</text>
</view>
<text v-if="diyComponent.mode != 'img'"
class="graphic-text w-full text-center truncate leading-normal"
:class="{ 'pt-1.5' : diyComponent.mode != 'text' }"
:style="{ fontSize: diyComponent.font.size * 2 + 'rpx', fontWeight: diyComponent.font.weight, color: diyComponent.font.color }">
{{ item.title }}
</text>
</view>
</view>
</template>
</swiper-item>
</swiper>
<scroll-view v-else :scroll-x="diyComponent.showStyle == 'singleSlide'" :class="['graphic-nav','graphic-nav-' + diyComponent.showStyle]" class=" py-[10rpx]">
<!-- #ifdef MP -->
<view class="uni-scroll-view-content">
<!-- #endif -->
<view class="graphic-nav-item" :class="{'flex-shrink-0' : diyComponent.showStyle == 'singleSlide'}"
v-for="(item, index) in diyComponent.list" :key="item.id"
:style="{ width: 100 / diyComponent.rowCount + '%' }">
<view @click="diyStore.toRedirect(item.link)" class="flex flex-col items-center box-border py-2">
<view class="graphic-img relative flex items-center justify-center w-10 h-10"
v-if="diyComponent.mode != 'text'"
:style="{ width: diyComponent.imageSize * 2 + 'rpx', height: diyComponent.imageSize * 2 + 'rpx' }">
<image v-if="item.imageUrl" :src="img(item.imageUrl)" mode="aspectFill"
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="aspectFill"
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
<text
:class="['tag absolute -top-[10rpx] -right-[24rpx] text-white rounded-[24rpx] rounded-bl-none transform scale-80 py-1 px-2 text-xs']"
v-if="item.label.control"
:style="{ color: item.label.textColor, backgroundImage: 'linear-gradient(' + item.label.bgColorStart + ',' + item.label.bgColorEnd + ')' }">
{{ item.label.text }}
</text>
</view>
<text v-if="diyComponent.mode != 'img'"
class="graphic-text w-full text-center truncate leading-normal"
:class="{ 'pt-1.5' : diyComponent.mode != 'text' }"
:style="{ fontSize: diyComponent.font.size * 2 + 'rpx', fontWeight: diyComponent.font.weight, color: diyComponent.font.color }">
{{ item.title }}
</text>
</view>
</view>
<!-- #ifdef MP -->
</view>
<!-- #endif -->
</scroll-view>
</view>
</view>
</template>
<script lang="ts" setup>
//
import { ref,computed, watch, onMounted, nextTick,getCurrentInstance } from 'vue';
import { img } from '@/utils/common';
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
//
const maskLayer = computed(()=>{
var style = '';
if(diyComponent.value.componentBgUrl) {
style += 'position:absolute;top:0;width:100%;';
style += `background: rgba(0,0,0,${diyComponent.value.componentBgAlpha / 10});`;
style += `height:${height.value}px;`;
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
}
return style;
});
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
const swiperIndex = ref(0);
const swiperChange = e => {
swiperIndex.value = e.detail.current;
};
const swiperCondition = (index, numItem) => {
let count = diyComponent.value.pageCount * diyComponent.value.rowCount;
let result = true;
// #ifdef MP-WEIXIN
result = index >= [(numItem) * (count)] && index < [(numItem + 1) * (count)];
// #endif
// #ifdef H5
result = index >= [(numItem - 1) * (count)] && index < [numItem * (count)];
// #endif
return result;
}
const swiperHeight = ref('');
const handleData = () => {
if(diyComponent.value.layout == 'horizontal' && diyComponent.value.showStyle == 'pageSlide') {
var height = 0;
const query = uni.createSelectorQuery().in(instance);
query.select('.graphic-nav-item').boundingClientRect((data: any) => {
height = data.height * diyComponent.value.pageCount;
swiperHeight.value = (height * 2) + 'rpx';
}).exec();
}
};
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'GraphicNav') {
refresh();
}
}
)
}
});
const instance = getCurrentInstance();
const height = ref(0)
const refresh = () => {
nextTick(() => {
handleData()
const query = uni.createSelectorQuery().in(instance);
query.select('.diy-graphic-nav').boundingClientRect((data: any) => {
height.value = data.height;
}).exec();
})
}
</script>
<style>
/* 固定显示 */
.graphic-nav-fixed>>>.uni-scroll-view-content {
display: flex;
flex-wrap: wrap;
}
/* 单行滑动 */
.graphic-nav-singleSlide>>>.uni-scroll-view-content {
display: flex;
}
</style>
<style lang="scss" scoped>
</style>

44
src/app/components/diy/horz-blank/index.vue

@ -1,44 +0,0 @@
<template>
<view :style="warpCss"></view>
</template>
<script setup lang="ts">
//
import { computed, watch } from 'vue';
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'height:' + diyComponent.value.height * 2 + 'rpx;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
</script>
<style></style>

40
src/app/components/diy/horz-line/index.vue

@ -1,40 +0,0 @@
<template>
<view class="horz-line-wrap">
<view v-if="diyStore.mode == 'decorate'" class="h-[30rpx]"></view>
<view :style="warpCss"></view>
<view v-if="diyStore.mode == 'decorate'" class="h-[30rpx]"></view>
</view>
</template>
<script setup lang="ts">
// 线
import { computed, watch } from 'vue';
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'border-top:' + (diyComponent.value.borderWidth * 2) + 'rpx ' + diyComponent.value.borderStyle + ' ' + diyComponent.value.borderColor + ';';
return style;
})
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
</script>
<style></style>

91
src/app/components/diy/hot-area/index.vue

@ -1,91 +0,0 @@
<template>
<view :style="warpCss">
<view class="simple-graph-wrap overflow-hidden relative leading-0">
<image v-if="diyComponent.imageUrl" :style="itemCss" :src="img(diyComponent.imageUrl)" mode="widthFix" :show-menu-by-longpress="true" class="w-full"/>
<image v-else :style="itemCss" :src="img('static/resource/images/diy/figure.png')" mode="widthFix" :show-menu-by-longpress="true" class="w-full"/>
<template v-if="diyStore.mode != 'decorate'">
<!-- 热区功能 -->
<view @click="diyStore.toRedirect(mapItem.link)" class="absolute" v-for="(mapItem, mapIndex) in diyComponent.heatMapData"
:key="mapIndex" :style="{
width: mapItem.width + '%',
height: mapItem.height + '%',
left: mapItem.left + '%',
top: mapItem.top + '%'
}"></view>
</template>
</view>
</view>
</template>
<script setup lang="ts">
//
import { computed, watch, onMounted } from 'vue';
import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
return style;
})
const itemCss = computed(() => {
var style = '';
style += 'height:' + diyComponent.value.imgHeight + ';';
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
onMounted(() => {
refresh();
//
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'HotArea') {
refresh();
}
}
)
});
const refresh = () => {
if (diyStore.mode == 'decorate') {
//
if (diyComponent.value.imageUrl == '') {
diyComponent.value.imgWidth = 690;
diyComponent.value.imgHeight = 330;
}
}
}
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
</script>
<style lang="scss">
</style>

140
src/app/components/diy/image-ads/index.vue

@ -1,140 +0,0 @@
<template>
<view :style="warpCss">
<view :style="maskLayer"></view>
<view class="diy-image-ads">
<view v-if="diyComponent.list.length == 1" class="leading-0 overflow-hidden" :style="swiperWarpCss">
<view @click="diyStore.toRedirect(diyComponent.list[0].link)">
<image v-if="diyComponent.list[0].imageUrl" :src="img(diyComponent.list[0].imageUrl)" :style="{height: imgHeight}" mode="heightFix" class="!w-full" :show-menu-by-longpress="true"/>
<image v-else :src="img('static/resource/images/diy/figure.png')" :style="{height: imgHeight}" mode="heightFix" class="!w-full" :show-menu-by-longpress="true"/>
</view>
</view>
<swiper v-else class="swiper" :style="{ height: imgHeight }" autoplay="true" circular="true" @change="swiperChange">
<swiper-item class="swiper-item" v-for="(item) in diyComponent.list" :key="item.id" :style="swiperWarpCss">
<view @click="diyStore.toRedirect(item.link)">
<view class="item" :style="{height: imgHeight}">
<image v-if="item.imageUrl" :src="img(item.imageUrl)" mode="scaleToFill" class="w-full h-full" :show-menu-by-longpress="true"/>
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="scaleToFill" class="w-full h-full" :show-menu-by-longpress="true"/>
</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
</template>
<script lang="ts" setup>
// 广
import { ref,computed, watch, onMounted, nextTick,getCurrentInstance } from 'vue';
import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
return style;
})
const swiperWarpCss = computed(() => {
var style = '';
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
//
const maskLayer = computed(()=>{
var style = '';
if(diyComponent.value.componentBgUrl) {
style += 'position:absolute;top:0;width:100%;';
style += `background: rgba(0,0,0,${diyComponent.value.componentBgAlpha / 10});`;
style += `height:${height.value}px;`;
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
}
return style;
});
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
const imgHeight = computed(() => {
return (diyComponent.value.imageHeight * 2) + 'rpx';
})
const swiperIndex = ref(0);
const swiperChange = e => {
swiperIndex.value = e.detail.current;
};
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'ImageAds') {
refresh();
}
}
)
}
});
const instance = getCurrentInstance();
const height = ref(0)
const refresh = () => {
//
if (diyStore.mode == 'decorate') {
diyComponent.value.list.forEach((item : any) => {
if (item.imageUrl == '') {
item.imgWidth = 690;
item.imgHeight = 330;
}
});
}
nextTick(() => {
const query = uni.createSelectorQuery().in(instance);
query.select('.diy-image-ads').boundingClientRect((data: any) => {
height.value = data.height;
}).exec();
})
}
</script>
<style lang="scss" scoped>
</style>

185
src/app/components/diy/member-info/index.vue

@ -1,185 +0,0 @@
<template>
<view :style="warpCss">
<view class="pt-[34rpx] member-info">
<!-- #ifdef MP-WEIXIN -->
<!-- 解决fixed定位后导航栏塌陷的问题 -->
<view :style="navbarInnerStyle"></view>
<!-- #endif -->
<view v-if="info" class="flex ml-[32rpx] mr-[52rpx] items-center relative">
<!-- 唤起获取微信 -->
<u-avatar :src="img(info.headimg)" size="55" leftIcon="none" @click="clickAvatar"></u-avatar>
<view class="ml-[22rpx]">
<view class="text-[#222222] flex pr-[50rpx] flex-wrap items-center">
<view class="text-[#222222] truncate max-w-[320rpx] font-bold text-lg mr-[16rpx]" :style="{ color : diyComponent.textColor }">{{ info.nickname }}</view>
</view>
<view class="text-[#696B70] text-[24rpx] mt-[10rpx]" :style="{ color : diyComponent.textColor }">UID{{ info.member_no }}</view>
</view>
<view class="set-icon flex items-center absolute right-0 top-2">
<view @click="redirect({ url: '/app/pages/setting/index' })">
<text class="nc-iconfont nc-icon-shezhiV6xx-1 text-[40rpx] ml-[10rpx]" :style="{ color : diyComponent.textColor }"></text>
</view>
</view>
</view>
<view v-else class="flex ml-[32rpx] mr-[52rpx] items-center relative" @click="toLogin">
<u-avatar src="" size="55"></u-avatar>
<view class="ml-[22rpx]">
<view class="text-[#222222] font-bold text-lg" :style="{ color : diyComponent.textColor }">
{{ t('login') }}/{{ t('register') }}
</view>
</view>
<view class="set-icon flex items-center absolute right-0 top-2">
<view @click="redirect({ url: '/app/pages/setting/index' })">
<text class="nc-iconfont nc-icon-shezhiV6xx-1 text-[40rpx] ml-[10rpx]" :style="{ color : diyComponent.textColor }"></text>
</view>
</view>
</view>
<view class="flex m-[30rpx] mb-0 py-[30rpx] items-center">
<view class="flex-1 text-center">
<view class="font-bold">
<view @click="redirect({ url: info ? '/app/pages/member/balance' : '' })" :style="{ color : diyComponent.textColor }">{{ money }}</view>
</view>
<view class="text-sm mt-[10rpx]">
<view @click="redirect({ url: info ? '/app/pages/member/balance' : '' })" :style="{ color : diyComponent.textColor }">{{ t('balance') }}</view>
</view>
</view>
<view class="border-solid border-white border-l border-b-0 border-t-0 border-r-0 h-[60rpx]"></view>
<view class="flex-1 text-center">
<view class="font-bold">
<view @click="redirect({ url: info ? '/app/pages/member/point' : '' })" :style="{ color : diyComponent.textColor }">{{ parseInt(info?.point) || 0 }}</view>
</view>
<view class="text-sm mt-[10rpx]">
<view @click="redirect({ url: info ? '/app/pages/member/point' : '' })" :style="{ color : diyComponent.textColor }">{{ t('point') }}</view>
</view>
</view>
</view>
</view>
<!-- #ifdef MP-WEIXIN -->
<information-filling ref="infoFill"></information-filling>
<!-- #endif -->
</view>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue'
import useMemberStore from '@/stores/member'
import { useLogin } from '@/hooks/useLogin'
import { img, isWeixinBrowser, redirect, urlDeconstruction, moneyFormat } from '@/utils/common'
import { t } from '@/locale'
import { wechatSync } from '@/app/api/system'
import useDiyStore from '@/app/stores/diy'
const props = defineProps(['component', 'index', 'pullDownRefreshCount','global']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if (diyComponent.value.bgUrl) {
style += 'background-image:url(' + img(diyComponent.value.bgUrl) + ');';
style += 'background-size: 100%;';
style += 'background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
const memberStore = useMemberStore()
// #ifdef H5
const { query } = urlDeconstruction(location.href)
if (query.code && isWeixinBrowser()) {
wechatSync({ code: query.code }).then(res => {
memberStore.getMemberInfo()
})
}
// #endif
const info = computed(() => {
//
if (diyStore.mode == 'decorate') {
return {
headimg: '',
nickname: '昵称',
balance: 0,
point: 0,
money: 0,
member_no: 'NIU0000021'
}
} else {
return memberStore.info;
}
})
const money = computed(() => {
if (info.value) {
let m = parseFloat(info.value.balance) + parseFloat(info.value.money)
return moneyFormat(m.toString());
} else {
return 0;
}
})
const toLogin = () => {
useLogin().setLoginBack({ url: '/app/pages/member/index' })
}
const infoFill = ref(false)
const clickAvatar = () => {
// #ifdef MP-WEIXIN
infoFill.value.show = true
// #endif
// #ifdef H5
if (isWeixinBrowser()) {
useLogin().getAuthCode('snsapi_userinfo')
} else {
redirect({ url: '/app/pages/member/personal' })
}
// #endif
}
let menuButtonInfo = {};
// (API)
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
// #endif
//
const navbarInnerStyle = computed(() => {
let style = '';
//
// #ifdef MP
if (props.global.topStatusBar.isShow == false) {
style += 'height:' + menuButtonInfo.height + 'px;';
style += 'padding-top:' + menuButtonInfo.top + 'px;';
}
// #endif
return style;
})
</script>
<style lang="scss" scoped>
</style>

220
src/app/components/diy/member-level/index.vue

@ -1,220 +0,0 @@
<template>
<view :style="warpCss" class="overflow-hidden" v-if="info && list && list.length">
<view v-if="diyComponent.style == 'style-1'" class="rounded-t-[16rpx] flex items-center justify-between style-bg-1 py-[20rpx] px-[30rpx]">
<view class="flex items-end">
<image :src="img('static/resource/images/diy/member/VIP_02.png')" mode="aspectFit" class="w-[50rpx] h-[36rpx]" />
<text class="text-[28rpx] text-[#FFDAA8] ml-[10rpx] font-bold max-w-[440rpx] truncate">{{info.member_level_name}}</text>
</view>
<view class="flex items-center justify-center rounded-[30rpx] box-border style-btn w-[120rpx] h-[50rpx]" @click="toLink('/app/pages/member/level')">
<text class="text-[24rpx] text-[#333]">{{ info.member_level ? (upgradeGrowth > 0 ? '去升级' : '去查看') : '去解锁' }}</text>
<text class="iconfont iconxiayibu1 ml-[2rpx] !text-[20rpx] text-[#333]"></text>
</view>
</view>
<view v-if="diyComponent.style == 'style-2'" class="rounded-[16rpx] flex items-center justify-between style-bg-2 p-[30rpx]">
<view class="flex flex-col">
<view class="flex items-center">
<image :src="img('static/resource/images/diy/member/VIP_01.png')" mode="aspectFit" class="w-[74rpx] h-[30rpx]" />
<text class="text-[32rpx] text-[#FFE3B1] leading-[normal] ml-[14rpx] font-bold max-w-[420rpx] truncate">{{info.member_level_name}}</text>
</view>
<text class="text-[#fff] text-[24rpx] mt-[10rpx] leading-[32rpx]" v-if="benefits_arr && benefits_arr.length">{{info.member_level_name}}购物享{{benefits_arr[0].title}}</text>
</view>
<view class="flex items-center justify-center rounded-[30rpx] box-border style-btn w-[120rpx] h-[50rpx]" @click="toLink('/app/pages/member/level')">
<text class="text-[24rpx] text-[#333]">{{ info.member_level ? (upgradeGrowth > 0 ? '去升级' : '去查看') : '去解锁' }}</text>
<text class="iconfont iconxiayibu1 ml-[2rpx] !text-[20rpx] text-[#333]"></text>
</view>
</view>
<view v-if="diyComponent.style == 'style-3'" class="rounded-[16rpx] style-bg-3 p-[30rpx]">
<view class="flex items-center justify-between style-border-3 mb-[22rpx] pb-[22rpx]">
<view class="flex flex-col">
<view class="flex items-center">
<view class="flex justify-end leading-[30rpx] box-border text-[#fff] pr-[10rpx] text-[26rpx] w-[120rpx] h-[30rpx] bg-contain bg-no-repeat" :style="{'backgroundImage': 'url('+img('static/resource/images/diy/member/VIP.png')+')'}">
VIP.{{currIndex}}
</view>
<text class="text-[#733F02] ml-[8rpx] text-[30rpx] font-bold max-w-[380rpx] truncate">{{info.member_level_name}}</text>
</view>
<text class="text-[28rpx] text-[#794200] mt-[16rpx]">购物或邀请好友可以提升等级</text>
</view>
<view class="flex items-center" @click="toLink('/app/pages/member/level')">
<view class="flex flex-col items-center justify-center">
<image :src="img('static/resource/images/diy/member/rule.png')" mode="aspectFit" class="w-[26rpx] h-[26rpx]" />
<text class="text-[24rpx] text-[#733F02] mt-[10rpx]">规则</text>
</view>
<view class="ml-[10rpx] text-[#733F02] !text-[26rpx] nc-iconfont nc-icon-youV6xx"></view>
</view>
</view>
<view class="flex items-center justify-between">
<view class="flex flex-col flex-1">
<view class="overflow-hidden rounded-[20rpx]">
<progress :percent="progress()" activeColor="#fff" backgroundColor="rgba(255,5,5,.1)" stroke-width="6" />
</view>
<text class="text-[24rpx] text-[#794200] mt-[16rpx]" v-if="upgradeGrowth > 0">还差{{upgradeGrowth}}成长值即可升级为{{ list[afterCurrIndex].level_name }}</text>
<text class="text-[24rpx] text-[#794200] mt-[16rpx]" v-else>恭喜您升级为最高等级</text>
</view>
<view class="flex items-center rounded-[30rpx] bg-[rgb(245,230,185)] p-[16rpx] ml-[40rpx]" @click="toLink('/app/pages/member/level')">
<text class="text-[28rpx] text-[#733F02]">{{info.member_level ? (upgradeGrowth > 0 ? '做任务' : '点击查看') : '去解锁'}}</text>
<image :src="img('static/resource/images/diy/member/vector.png')" mode="aspectFit" class="ml-[8rpx] w-[12rpx] h-[18rpx]" />
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue'
import { img, redirect } from '@/utils/common'
import useMemberStore from '@/stores/member'
import { t } from '@/locale'
import { getMemberLevel } from '@/app/api/member';
import useDiyStore from '@/app/stores/diy'
const props = defineProps(['component', 'index', 'pullDownRefreshCount']);
const diyStore = useDiyStore();
const memberStore = useMemberStore()
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
//
let list:any = ref([])
let upgradeGrowth = ref(0) //
let currIndex = ref(0) //
let afterCurrIndex = ref(-1) //
let benefits_arr:any = ref([]) //
const info:any = computed(() => {
//
if (diyStore.mode == 'decorate') {
upgradeGrowth.value = 0;
benefits_arr.value = [{'title': '商品包邮'}];
currIndex.value = 1;
list.value = [{}];
return {
member_level_name: '会员等级',
growth: 5
}
} else {
if (memberStore.info) {
getMemberLevelFn();
}
return memberStore.info;
}
})
const getMemberLevelFn = ()=> {
getMemberLevel().then((res: any) => {
list.value = res.data;
if(!list.value.length) return false;
let isSet = false;
//
if (info.value && list.value && list.value.length) {
list.value.forEach((item: any, index: any) => {
if (item.level_id == info.value.member_level) {
currIndex.value = index + 1;
//
if (item.level_benefits) {
Object.values(item.level_benefits).forEach((bItem: any) => {
if (bItem.content) {
benefits_arr.value.push(bItem.content)
}
})
}
}
if (item.growth > info.value.growth && !isSet) {
afterCurrIndex.value = index;
isSet = true;
}
})
}
if (info.value.member_level) {
if(afterCurrIndex.value == -1){
afterCurrIndex.value = list.value.length - 1;
}
if (list.value[afterCurrIndex.value] && list.value[afterCurrIndex.value].growth) {
upgradeGrowth.value = list.value[afterCurrIndex.value].growth - info.value.growth;
}
}else{
//
info.value.member_level_name = list.value[0].level_name;
upgradeGrowth.value = list.value[0].growth;
afterCurrIndex.value = 0;
currIndex.value = 1;
}
})
}
//
let progress = () => {
let num = 100
if(list.value[afterCurrIndex.value] && list.value[afterCurrIndex.value].growth) {
if(info.value.growth) {
num = info.value.growth / list.value[afterCurrIndex.value].growth * 100
}else{
num = 0
}
}
return num
}
//
const toLink = (link: string)=>{
if (diyStore.mode == 'decorate') return false;
redirect({ url: link })
}
</script>
<style lang="scss" scoped>
.style-bg-1{
background: linear-gradient(to right, #1F1313, #4D4646);
}
.style-btn{
background: linear-gradient(to right, #FFEACB, #FFD195);
}
.style-bg-2{
background: linear-gradient(to right, #484846, #222222);
border-bottom-left-radius: 320rpx 16rpx;
border-bottom-right-radius: 320rpx 16rpx;
}
.style-bg-3{
background: linear-gradient(to right, #FFE6C2, #E39F42);
}
.style-border-3{
position: relative;
&:after{
content: "";
position: absolute;
background: linear-gradient(to right, #F0D2A9, #DBA051);
height: 2rpx;
bottom: 0;
left: 0;
right: 0;
}
}
</style>

291
src/app/components/diy/notice/index.vue

@ -1,291 +0,0 @@
<template>
<view :style="warpCss">
<view :style="maskLayer"></view>
<view class="diy-notice relative overflow-hidden">
<view class="flex items-center pl-[28rpx] p-[22rpx]">
<template v-if="diyComponent.noticeType == 'img'">
<template v-if="diyComponent.imgType == 'system'">
<image v-if="diyComponent.systemUrl == 'style_1'" :src="img(`static/resource/images/diy/notice/${diyComponent.systemUrl}.png`)" class="h-[40rpx] max-w-[130rpx] mr-[20rpx] flex-shrink-0" mode="heightFix"/>
<image v-else-if="diyComponent.systemUrl == 'style_2'" :src="img(`static/resource/images/diy/notice/${diyComponent.systemUrl}.png`)" class="w-[200rpx] mr-[20rpx] h-[40rpx] flex-shrink-0" mode="heightFix" />
</template>
<image v-else-if="diyComponent.imgType == 'diy'" :src="img(diyComponent.imageUrl || '')" class="w-[50rpx] h-[50rpx] mr-[20rpx] flex-shrink-0" mode="heightFix"/>
</template>
<view v-if="diyComponent.noticeType == 'text' && diyComponent.noticeTitle" class="max-w-[128rpx] px-[12rpx] text-[26rpx] h-[40rpx] leading-[40rpx] text-[var(--primary-color)] bg-[var(--primary-color-light)] truncate rounded-[8rpx] mr-[20rpx] flex-shrink-0">{{ diyComponent.noticeTitle }}</view>
<view class="flex-1 flex overflow-hidden horizontal-body" id="horizontal-body" :class="{'items-center':diyComponent.scrollWay == 'upDown'}">
<!-- 横向滚动 -->
<view class="horizontal-wrap" :style="marqueeStyle" v-if="diyComponent.scrollWay == 'horizontal'">
<view class="marquee marquee-one" id="marquee-one" >
<view class="item flex-shrink-0 !leading-[40rpx] h-[40rpx]" :class="{'ml-[80rpx]':index}" v-for="(item, index) in diyComponent.list" :key="index" @click="toRedirect(item)" :style="{ color: diyComponent.textColor, fontSize: diyComponent.fontSize * 2 + 'rpx', fontWeight: diyComponent.fontWeight }">{{ item.text }}</view>
</view>
<view class="marquee">
<view class="item flex-shrink-0 !leading-[40rpx] h-[40rpx]" :class="{'ml-[80rpx]':index}" v-for="(item, index) in diyComponent.list" :key="index" @click="toRedirect(item)" :style="{ color: diyComponent.textColor, fontSize: diyComponent.fontSize * 2 + 'rpx', fontWeight: diyComponent.fontWeight }">{{ item.text }}</view>
</view>
</view>
<!-- 上下滚动 -->
<template v-if="diyComponent.scrollWay == 'upDown'">
<swiper :vertical="true" :duration="500" autoplay="true" circular="true" class="flex-1">
<swiper-item v-for="(item, index) in diyComponent.list" :key="index" @touchmove.prevent.stop>
<text @click="toRedirect(item)" class="beyond-hiding using-hidden" :style="{ color: diyComponent.textColor, fontSize: diyComponent.fontSize * 2 + 'rpx', fontWeight: diyComponent.fontWeight }">
{{ item.text }}
</text>
</swiper-item>
</swiper>
<text class="nc-iconfont nc-icon-youV6xx text-[26rpx] -ml-[8rpx] pl-[30rpx]" :style="{'color': '#999', 'fontWeight': diyComponent.fontWeight}"></text>
</template>
</view>
</view>
<u-popup :show="noticeShow" @close="noticeShow = false" mode="center" :round="5" :safeAreaInsetBottom="false">
<view @touchmove.prevent.stop>
<view class="py-[25rpx] text-sm leading-none border-0 border-solid border-b-[2rpx] border-[#eee] flex items-center justify-between">
<text class="ml-[30rpx]">公告</text>
<text class="mr-[20rpx] nc-iconfont nc-icon-guanbiV6xx text-[35rpx]" @click="noticeShow = false"></text>
</view>
<scroll-view scroll-y="true" class="px-6 py-3 w-[480rpx] h-[500rpx] text-sm">{{ noticeContent }}</scroll-view>
<button @click="noticeShow = false" class="!mx-[30rpx] !mb-[40rpx] !w-auto !h-[70rpx] text-[24rpx] leading-[70rpx] rounded-full text-white !bg-[#ff4500] !text-[#fff]">我知道了</button>
</view>
</u-popup>
</view>
</view>
</template>
<script setup lang="ts">
//
import { ref,computed, watch, onMounted, nextTick,getCurrentInstance } from 'vue';
import { img } from '@/utils/common';
import useDiyStore from '@/app/stores/diy';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']);
const diyStore = useDiyStore();
const noticeShow = ref(false);
const noticeContent = ref('');
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
//
const maskLayer = computed(()=>{
var style = '';
if(diyComponent.value.componentBgUrl) {
style += 'position:absolute;top:0;width:100%;';
style += `background: rgba(0,0,0,${diyComponent.value.componentBgAlpha / 10});`;
style += `height:${height.value}px;`;
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
}
return style;
});
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
const marqueeBodyWidth = ref(0); //
const marqueeStyle = ref(''); //
const time = ref(0); //
const delayTime = ref(800); //
// pxrpx
const pxToRpx=(px)=> {
const screenWidth = uni.getSystemInfoSync().screenWidth
return (750 * Number.parseInt(px)) / screenWidth
}
//
const bindCrossSlipEvent = ()=> {
if (diyComponent.value.scrollWay == 'horizontal') {
setTimeout(() => {
nextTick(() => {
// #ifdef MP-WEIXIN
uni.createSelectorQuery().in(instance).select('.horizontal-body').boundingClientRect(res => {
marqueeBodyWidth.value = res.width;
console.log(pxToRpx(res.width))
const query = uni.createSelectorQuery().in(instance);
query.selectAll('.marquee-one').boundingClientRect((data:any) => {
let width = data[0].width
time.value = pxToRpx(width) * 10;
if (marqueeBodyWidth.value > width) {
marqueeStyle.value = `animation: none;`;
} else {
marqueeStyle.value = `
animation-duration: ${time.value}ms;
animation-delay: ${delayTime.value}ms;`;
}
}).exec();
}).exec();
// #endif
// #ifdef H5
marqueeBodyWidth.value = window.document.getElementById('horizontal-body').offsetWidth;
const width = window.document.getElementById('marquee-one').offsetWidth;
time.value = pxToRpx(width) * 10;
if (marqueeBodyWidth.value > width) {
marqueeStyle.value = `animation: none;`;
} else {
marqueeStyle.value = `
animation-duration: ${time.value}ms;
animation-delay: ${delayTime.value}ms;`;
}
// #endif
});
});
}
}
onMounted(() => {
bindCrossSlipEvent();
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'Notice') {
bindCrossSlipEvent();
refresh();
}
}
)
}
});
const instance = getCurrentInstance();
const height = ref(0)
const refresh = ()=> {
nextTick(() => {
const query = uni.createSelectorQuery().in(instance);
query.select('.diy-notice').boundingClientRect((data: any) => {
height.value = data.height;
}).exec();
})
}
const toRedirect = (data: {}) => {
if (diyStore.mode == 'decorate') return false;
if (diyComponent.value.showType == 'popup') {
noticeShow.value = true;
noticeContent.value = data.text;
} else {
diyStore.toRedirect(data);
}
}
</script>
<style lang="scss" scoped>
.main-wrap {
display: inline-block;
width: calc(100% - 100rpx);
position: relative;
}
swiper {
height: 50rpx;
}
.beyond-hiding {
display: inline-block;
width: 100%;
white-space: nowrap;
line-height: 50rpx
}
.notice-popup {
padding: 0 30rpx 40rpx;
background-color: #fff;
.head-wrap {
font-size: 32rpx;
line-height: 100rpx;
height: 100rpx;
display: block;
text-align: center;
position: relative;
border-bottom: 2rpx solid #eeeeee;
margin-bottom: 20rpx;
.iconfont {
position: absolute;
float: right;
right: 0;
font-size: 32rpx;
}
}
.content-wrap {
max-height: 600rpx;
overflow-y: auto;
}
button {
margin-top: 40rpx;
}
}
.horizontal-wrap {
height: 40rpx;
display: flex;
align-items: center;
transform: translateZ(0);
animation: marquee 0s 0s linear infinite;
}
.marquee {
display: flex;
align-items: center;
height: 100%;
white-space: nowrap;
padding-right: 60rpx;
// backface-visibility: hidden;
// -webkit-perspective: 1000;
// -moz-perspective: 1000;
// -ms-perspective: 1000;
// perspective: 1000;
}
@keyframes marquee {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
</style>

125
src/app/components/diy/rich-text/index.vue

@ -1,125 +0,0 @@
<template>
<view :style="warpCss">
<view :style="maskLayer"></view>
<view class="diy-rich-text relative">
<view v-if="diyComponent.html && diyComponent.html != '<p><br></p>'">
<u-parse :content="diyComponent.html" :tagStyle="{img: 'vertical-align: top;'}"></u-parse>
</view>
<template v-else>
<view>点此编辑富文本内容 &gt;</view>
<view>
<text>你可以对文字进行</text>
<text></text>
<text class="font-bold">加粗</text>
<text></text>
<text class="italic">斜体</text>
<text></text>
<text class="underline">下划线</text>
<text></text>
<text class="line-through">删除线</text>
<text>文字</text>
<text style="color: rgb(0, 176, 240);">颜色</text>
<text></text>
<text style="background-color: rgb(255, 192, 0); color: rgb(255, 255, 255);">背景色</text>
<text>以及字号</text>
<text class="text-lg"></text>
<text class="text-sm"></text>
<text class="pl-[10rpx]">等简单排版操作</text>
</view>
<view>也可在这里插入图片并对图片加上超级链接方便用户点击</view>
</template>
</view>
</view>
</template>
<script setup lang="ts">
//
import { ref,computed, watch, onMounted, nextTick,getCurrentInstance } from 'vue';
import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
//
const maskLayer = computed(()=>{
var style = '';
if(diyComponent.value.componentBgUrl) {
style += 'position:absolute;top:0;width:100%;';
style += `background: rgba(0,0,0,${diyComponent.value.componentBgAlpha / 10});`;
style += `height:${height.value}px;`;
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
}
return style;
});
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'RichText') {
refresh();
}
}
)
}
});
const instance = getCurrentInstance();
const height = ref(0)
const refresh = ()=> {
nextTick(() => {
const query = uni.createSelectorQuery().in(instance);
query.select('.diy-rich-text').boundingClientRect((data: any) => {
height.value = data.height;
}).exec();
})
}
</script>
<style></style>

590
src/app/components/diy/rubik-cube/index.vue

@ -1,590 +0,0 @@
<template>
<view :style="warpCss">
<view :style="maskLayer"></view>
<view :class="['rubik-cube relative', diyStore.mode]">
<!-- 1左2右 -->
<template v-if="diyComponent.mode == 'row1-lt-of2-rt'">
<view class="template-left">
<view @click="diyStore.toRedirect(diyComponent.list[0].link)" :class="['item', diyComponent.mode]"
:style="{ marginRight: diyComponent.imageGap * 2 + 'rpx', width: diyComponent.list[0].imgWidth, height: diyComponent.list[0].imgHeight + 'px' }">
<image v-if="diyComponent.list[0].imageUrl" :src="img(diyComponent.list[0].imageUrl)" mode="scaleToFill" :style="diyComponent.list[0].pageItemStyle" :show-menu-by-longpress="true"/>
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="scaleToFill" :style="diyComponent.list[0].pageItemStyle" :show-menu-by-longpress="true"/>
</view>
</view>
<view class="template-right">
<template v-for="(item, index) in diyComponent.list" :key="index">
<template v-if="index > 0">
<view @click="diyStore.toRedirect(item.link)" :class="['item', diyComponent.mode]"
:style="{ marginBottom: diyComponent.imageGap * 2 + 'rpx', width: item.imgWidth, height: item.imgHeight + 'px' }">
<image v-if="item.imageUrl" :src="img(item.imageUrl)" mode="scaleToFill" :style="item.pageItemStyle" :show-menu-by-longpress="true"/>
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="scaleToFill" :style="item.pageItemStyle" :show-menu-by-longpress="true"/>
</view>
</template>
</template>
</view>
</template>
<!-- 1左3右 -->
<template v-else-if="diyComponent.mode == 'row1-lt-of1-tp-of2-bm'">
<view class="template-left">
<view @click="diyStore.toRedirect(diyComponent.list[0].link)" :class="['item', diyComponent.mode]"
:style="{ marginRight: diyComponent.imageGap * 2 + 'rpx', width: diyComponent.list[0].imgWidth, height: diyComponent.list[0].imgHeight + 'px' }">
<image v-if="diyComponent.list[0].imageUrl" :src="img(diyComponent.list[0].imageUrl)" mode="scaleToFill" :style="diyComponent.list[0].pageItemStyle" :show-menu-by-longpress="true"/>
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="scaleToFill" :style="diyComponent.list[0].pageItemStyle" :show-menu-by-longpress="true"/>
</view>
</view>
<view class="template-right">
<view @click="diyStore.toRedirect(diyComponent.list[1].link)" :class="['item', diyComponent.mode]"
:style="{ marginBottom: diyComponent.imageGap * 2 + 'rpx', width: diyComponent.list[1].imgWidth, height: diyComponent.list[1].imgHeight + 'px' }">
<image v-if="diyComponent.list[1].imageUrl" :src="img(diyComponent.list[1].imageUrl)" mode="scaleToFill" :style="diyComponent.list[1].pageItemStyle" :show-menu-by-longpress="true"/>
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="scaleToFill" :style="diyComponent.list[1].pageItemStyle" :show-menu-by-longpress="true"/>
</view>
<view class="template-bottom">
<template v-for="(item, index) in diyComponent.list" :key="index">
<template v-if="index > 1">
<view @click="diyStore.toRedirect(item.link)" :class="['item', diyComponent.mode]" :style="{
marginRight: diyComponent.imageGap * 2 + 'rpx',
width: item.imgWidth,
height: item.imgHeight + 'px'
}">
<image v-if="item.imageUrl" :src="img(item.imageUrl)" mode="scaleToFill" :style="item.pageItemStyle" :show-menu-by-longpress="true"/>
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="scaleToFill" :style="item.pageItemStyle" :show-menu-by-longpress="true"/>
</view>
</template>
</template>
</view>
</view>
</template>
<template v-else>
<view :class="['item', diyComponent.mode]" v-for="(item, index) in diyComponent.list" :key="index"
@click="diyStore.toRedirect(item.link)"
:style="{ marginRight: diyComponent.imageGap * 2 + 'rpx', marginBottom: diyComponent.imageGap * 2 + 'rpx', width: item.widthStyle, height: item.imgHeight + 'px' }">
<image v-if="item.imageUrl" :src="img(item.imageUrl)" :mode="'scaleToFill'" :style="item.pageItemStyle" :show-menu-by-longpress="true"/>
<image v-else :src="img('static/resource/images/diy/figure.png')" :mode="'scaleToFill'" :style="item.pageItemStyle" :show-menu-by-longpress="true"/>
</view>
</template>
</view>
</view>
</template>
<script setup lang="ts">
//
import { ref,onMounted, computed, watch,nextTick,getCurrentInstance } from 'vue';
import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
/**
* 处理rpx渲染之后变成rem存在小数的问题
* @param rpx
*/
const upx2px = (rpx: number) => {
return uni.upx2px(rpx) + 1
}
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
//
const maskLayer = computed(()=>{
var style = '';
if(diyComponent.value.componentBgUrl) {
style += 'position:absolute;top:0;width:100%;';
style += `background: rgba(0,0,0,${diyComponent.value.componentBgAlpha / 10});`;
style += `height:${height.value}px;`;
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
}
return style;
});
const countBorderRadius = (type, index) => {
var obj = '';
if (diyComponent.value.elementAngle == 'right') {
return obj;
}
var defaultData:any = {
'row1-lt-of2-rt': [
['border-top-right-radius', 'border-bottom-right-radius'],
['border-top-left-radius', 'border-bottom-left-radius', 'border-bottom-right-radius'],
['border-top-left-radius', 'border-bottom-left-radius', 'border-top-right-radius']
],
'row1-lt-of1-tp-of2-bm': [
['border-top-right-radius', 'border-bottom-right-radius'],
['border-top-left-radius', 'border-bottom-left-radius', 'border-bottom-right-radius'],
['border-radius'],
['border-top-left-radius', 'border-bottom-left-radius', 'border-top-right-radius']
],
'row1-tp-of2-bm': [
['border-bottom-left-radius', 'border-bottom-right-radius'],
['border-top-left-radius', 'border-bottom-right-radius', 'border-top-right-radius'],
['border-top-left-radius', 'border-bottom-left-radius', 'border-top-right-radius']
],
'row2-lt-of2-rt': [
['border-top-right-radius', 'border-bottom-left-radius', 'border-bottom-right-radius'],
['border-top-left-radius', 'border-bottom-right-radius', 'border-bottom-left-radius'],
['border-top-left-radius', 'border-bottom-right-radius', 'border-top-right-radius'],
['border-top-left-radius', 'border-bottom-left-radius', 'border-top-right-radius']
],
'row1-of4': [
['border-top-right-radius', 'border-bottom-right-radius'],
['border-radius'],
['border-radius'],
['border-top-left-radius', 'border-bottom-left-radius']
],
'row1-of3': [
['border-top-right-radius', 'border-bottom-right-radius'],
['border-radius'],
['border-top-left-radius', 'border-bottom-left-radius']
],
'row1-of2': [
['border-top-right-radius', 'border-bottom-right-radius'],
['border-top-left-radius', 'border-bottom-left-radius']
]
};
defaultData[type][index].forEach((item, index) => {
obj += 'border-top-left-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;';
obj += 'border-top-right-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;';
obj += 'border-bottom-left-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;';
obj += 'border-bottom-right-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;';
});
return obj;
}
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'RubikCube') {
refresh();
}
}
)
}else{
watch(
() => diyComponent.value,
(newValue, oldValue) => {
refresh();
}
)
}
});
const instance = getCurrentInstance();
const height = ref(0)
const refresh = () => {
if (diyStore.mode == 'decorate') {
diyComponent.value.list.forEach((item : any) => {
//
if (item.imageUrl == '') {
item.imgWidth = 690;
item.imgHeight = 330;
}
});
}
handleData()
nextTick(() => {
const query = uni.createSelectorQuery().in(instance);
query.select('.rubik-cube').boundingClientRect((data: any) => {
height.value = data.height;
}).exec();
})
}
const handleData = () => {
var singleRow:any = {
'row1-of2': {
ratio: 2,
width: 'calc((100% - ' + upx2px(diyComponent.value.imageGap * 2) + 'px) / 2)'
},
'row1-of3': {
ratio: 3,
width: 'calc((100% - ' + upx2px(diyComponent.value.imageGap * 4) + 'px) / 3)'
},
'row1-of4': {
ratio: 4,
width: 'calc((100% - ' + upx2px(diyComponent.value.imageGap * 6) + 'px) / 4)'
}
};
diyComponent.value.list.forEach((item, index) => {
item.pageItemStyle = countBorderRadius(diyComponent.value.mode, index);
});
if (singleRow[diyComponent.value.mode]) {
calcSingleRow(singleRow[diyComponent.value.mode]);
} else if (diyComponent.value.mode == 'row2-lt-of2-rt') {
calcFourSquare();
} else if (diyComponent.value.mode == 'row1-lt-of2-rt') {
calcRowOneLeftOfTwoRight();
} else if (diyComponent.value.mode == 'row1-tp-of2-bm') {
calcRowOneTopOfTwoBottom();
} else if (diyComponent.value.mode == 'row1-lt-of1-tp-of2-bm') {
calcRowOneLeftOfOneTopOfTwoBottom();
}
};
/**
* 魔方单行多个平分宽度
* 公式
* 宽度屏幕宽度/2示例375/2=187.5
* 比例原图高/原图宽示例322/690=0.46
* 高度宽度*比例示例187.5*0.46=86.25
*/
const calcSingleRow = (params:any) => {
uni.getSystemInfo({
success: res => {
let maxHeight = 0;
diyComponent.value.list.forEach((item, index) => {
var ratio = item.imgHeight / item.imgWidth;
let width = res.windowWidth - upx2px(diyComponent.value.margin.both * 2); //
if (diyComponent.value.imageGap > 0) {
width -= upx2px(params.ratio * diyComponent.value.imageGap * 2); //
}
item.imgWidth = width / params.ratio;
item.imgHeight = item.imgWidth * ratio;
if (maxHeight == 0 || maxHeight < item.imgHeight) maxHeight = item.imgHeight;
})
diyComponent.value.list.forEach((item, index) => {
item.widthStyle = params.width;
item.imgHeight = maxHeight;
});
}
})
};
/**
* 魔方四方型各占50%
*/
const calcFourSquare = () => {
uni.getSystemInfo({
success: res => {
let maxHeightFirst = 0;
let maxHeightTwo = 0;
diyComponent.value.list.forEach((item, index) => {
var ratio = item.imgHeight / item.imgWidth;
item.imgWidth = res.windowWidth;
item.imgWidth -= upx2px(diyComponent.value.margin.both * 4);
if (diyComponent.value.imageGap > 0) {
item.imgWidth -= upx2px(diyComponent.value.imageGap * 2);
}
item.imgWidth = item.imgWidth / 2;
item.imgHeight = item.imgWidth * ratio;
//
if (index <= 1) {
if (maxHeightFirst == 0 || maxHeightFirst < item.imgHeight) {
maxHeightFirst = item.imgHeight;
}
} else if (index > 1) {
if (maxHeightTwo == 0 || maxHeightTwo < item.imgHeight) {
maxHeightTwo = item.imgHeight;
}
}
});
diyComponent.value.list.forEach((item, index) => {
item.imgWidth = 'calc((100% - ' + upx2px(diyComponent.value.imageGap * 2) + 'px) / 2)';
item.widthStyle = item.imgWidth;
if (index <= 1) {
item.imgHeight = maxHeightFirst;
} else if (index > 1) {
item.imgHeight = maxHeightTwo;
}
});
}
});
}
/**
* 魔方1左2右
*/
const calcRowOneLeftOfTwoRight = () => {
let rightHeight = 0; //
let divide = 'left'; // leftright
if (diyComponent.value.list[1].imgWidth === diyComponent.value.list[2].imgWidth) divide = 'right';
uni.getSystemInfo({
success: res => {
diyComponent.value.list.forEach((item, index) => {
if (index == 0) {
var ratio = item.imgHeight / item.imgWidth; //
item.imgWidth = res.windowWidth - upx2px(diyComponent.value.margin.both * 4) - upx2px(diyComponent.value.imageGap * 2);
item.imgWidth = item.imgWidth / 2;
item.imgHeight = item.imgWidth * ratio;
rightHeight = (item.imgHeight - upx2px(diyComponent.value.imageGap * 2)) / 2;
item.imgWidth += 'px';
} else {
item.imgWidth = diyComponent.value.list[0].imgWidth;
item.imgHeight = rightHeight;
}
});
}
});
}
/**
* 魔方1上2下
*/
const calcRowOneTopOfTwoBottom = () => {
var maxHeight = 0;
uni.getSystemInfo({
success: res => {
diyComponent.value.list.forEach((item, index) => {
var ratio = item.imgHeight / item.imgWidth; //
if (index == 0) {
item.imgWidth = res.windowWidth - upx2px(diyComponent.value.margin.both * 4);
} else if (index > 0) {
item.imgWidth = res.windowWidth - upx2px(diyComponent.value.margin.both * 4) - upx2px(diyComponent.value.imageGap * 2);
item.imgWidth = item.imgWidth / 2;
}
item.imgHeight = item.imgWidth * ratio;
//
if (index > 0 && (maxHeight == 0 || maxHeight < item.imgHeight)) maxHeight = item.imgHeight;
});
diyComponent.value.list.forEach((item, index) => {
item.imgWidth += 'px';
item.widthStyle = item.imgWidth;
if (index > 0) item.imgHeight = maxHeight;
});
}
});
}
/**
* 魔方1左3右
*/
const calcRowOneLeftOfOneTopOfTwoBottom = () => {
uni.getSystemInfo({
success: res => {
diyComponent.value.list.forEach((item, index) => {
//
if (index == 0) {
var ratio = item.imgHeight / item.imgWidth; //
item.imgWidth = res.windowWidth - upx2px(diyComponent.value.margin.both * 4) - upx2px(diyComponent.value.imageGap * 2);
item.imgWidth = item.imgWidth / 2;
item.imgHeight = item.imgWidth * ratio;
} else if (index == 1) {
item.imgWidth = diyComponent.value.list[0].imgWidth;
item.imgHeight = (diyComponent.value.list[0].imgHeight - upx2px(diyComponent.value.imageGap * 2)) / 2;
} else if (index > 1) {
item.imgWidth = (diyComponent.value.list[0].imgWidth - upx2px(diyComponent.value.imageGap * 2)) / 2;
item.imgHeight = diyComponent.value.list[1].imgHeight;
}
});
diyComponent.value.list.forEach((item, index) => {
item.imgWidth += 'px';
});
}
});
}
</script>
<style lang="scss">
.rubik-cube {
overflow: hidden;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.item {
text-align: center;
line-height: 0;
overflow: hidden;
image {
width: 100%;
max-width: 100%;
height: 100%;
}
}
}
//
.rubik-cube .item.row1-of2 {
box-sizing: border-box;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
.rubik-cube .item.row1-of2:nth-child(1) {
margin-left: 0 !important;
}
.rubik-cube .item.row1-of2:nth-child(2) {
margin-right: 0 !important;
}
//
.rubik-cube .item.row1-of3 {
box-sizing: border-box;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
.rubik-cube .item.row1-of3:nth-child(1) {
margin-left: 0 !important;
}
.rubik-cube .item.row1-of3:nth-child(3) {
margin-right: 0 !important;
}
//
.rubik-cube .item.row1-of4 {
box-sizing: border-box;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
.rubik-cube .item.row1-of4:nth-child(1) {
margin-left: 0 !important;
}
.rubik-cube .item.row1-of4:nth-child(4) {
margin-right: 0 !important;
}
//
.rubik-cube .item.row2-lt-of2-rt {
// width: 50%;
display: inline-block;
box-sizing: border-box;
}
.rubik-cube .item.row2-lt-of2-rt:nth-child(1) {
margin-left: 0 !important;
margin-top: 0 !important;
}
.rubik-cube .item.row2-lt-of2-rt:nth-child(2) {
margin-right: 0 !important;
margin-top: 0 !important;
}
.rubik-cube .item.row2-lt-of2-rt:nth-child(3) {
margin-left: 0 !important;
margin-bottom: 0 !important;
}
.rubik-cube .item.row2-lt-of2-rt:nth-child(4) {
margin-right: 0 !important;
margin-bottom: 0 !important;
}
//
.rubik-cube .template-left,
.rubik-cube .template-right {
// width: 50%;
box-sizing: border-box;
}
.rubik-cube .template-left .item.row1-lt-of2-rt:nth-child(1) {
margin-bottom: 0;
}
.rubik-cube .template-right .item.row1-lt-of2-rt:nth-child(2) {
margin-bottom: 0 !important;
}
.rubik-cube.row1-lt-of2-rt .template-right {
display: flex;
flex-direction: column;
justify-content: space-between;
}
//
.rubik-cube .item.row1-tp-of2-bm:nth-child(1) {
width: 100%;
box-sizing: border-box;
margin-top: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
.rubik-cube .item.row1-tp-of2-bm:nth-child(2) {
// width: 50%;
box-sizing: border-box;
margin-left: 0 !important;
margin-bottom: 0 !important;
}
.rubik-cube .item.row1-tp-of2-bm:nth-child(3) {
// width: 50%;
box-sizing: border-box;
margin-right: 0 !important;
margin-bottom: 0 !important;
}
//
.rubik-cube .template-left .item.row1-lt-of1-tp-of2-bm {
width: 100%;
box-sizing: border-box;
}
.rubik-cube .template-bottom {
display: flex;
align-items: center;
justify-content: space-between;
}
.rubik-cube .template-bottom .item:nth-child(2) {
margin-right: 0 !important;
}
</style>

129
src/app/components/diy/text/index.vue

@ -1,129 +0,0 @@
<template>
<view :style="warpCss">
<view :style="maskLayer"></view>
<view class="diy-text relative">
<view v-if="diyComponent.style == 'style-1'" class=" px-[20rpx]">
<view @click="diyStore.toRedirect(diyComponent.link)">
<view :style="{
fontSize: diyComponent.fontSize * 2 + 'rpx',
color: diyComponent.textColor,
fontWeight: diyComponent.fontWeight,
textAlign : diyComponent.textAlign
}">
{{ diyComponent.text }}
</view>
</view>
</view>
<view v-if="diyComponent.style == 'style-2'" class=" px-[20rpx] flex items-center">
<view @click="diyStore.toRedirect(diyComponent.link)">
<view class="max-w-[200rpx] truncate" :style="{
fontSize: diyComponent.fontSize * 2 + 'rpx',
color: diyComponent.textColor,
fontWeight: diyComponent.fontWeight
}">
{{ diyComponent.text }}
</view>
</view>
<text class="ml-[16rpx] max-w-[300rpx] truncate" :style="{ color: diyComponent.subTitle.color, fontSize: diyComponent.subTitle.fontSize * 2 + 'rpx', }">{{ diyComponent.subTitle.text }}</text>
<view class="ml-auto text-right " v-if="diyComponent.more.isShow" :style="{ color: diyComponent.more.color }">
<view @click="diyStore.toRedirect(diyComponent.more.link)" class="flex items-center">
<text class="max-w-[200rpx] truncate text-[24rpx]">{{ diyComponent.more.text }}</text>
<text class="nc-iconfont nc-icon-youV6xx text-[26rpx]" :style="{ color: diyComponent.more.color }"></text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
//
import { ref, computed, watch,onMounted,nextTick,getCurrentInstance } from 'vue';
import useDiyStore from '@/app/stores/diy';
import { img } from '@/utils/common';
const props = defineProps(['component', 'index', 'pullDownRefreshCount']);
const diyStore = useDiyStore();
const diyComponent = computed(() => {
if (diyStore.mode == 'decorate') {
return diyStore.value[props.index];
} else {
return props.component;
}
})
const warpCss = computed(() => {
var style = '';
style += 'position:relative;';
if(diyComponent.value.componentStartBgColor) {
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
}
if(diyComponent.value.componentBgUrl) {
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`;
style += 'background-size: cover;background-repeat: no-repeat;';
}
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
return style;
})
//
const maskLayer = computed(()=>{
var style = '';
if(diyComponent.value.componentBgUrl) {
style += 'position:absolute;top:0;width:100%;';
style += `background: rgba(0,0,0,${diyComponent.value.componentBgAlpha / 10});`;
style += `height:${height.value}px;`;
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
}
return style;
});
watch(
() => props.pullDownRefreshCount,
(newValue, oldValue) => {
//
}
)
onMounted(() => {
refresh();
//
if (diyStore.mode == 'decorate') {
watch(
() => diyComponent.value,
(newValue, oldValue) => {
if (newValue && newValue.componentName == 'Text') {
refresh();
}
}
)
}
});
const instance = getCurrentInstance();
const height = ref(0)
const refresh = ()=> {
nextTick(() => {
const query = uni.createSelectorQuery().in(instance);
query.select('.diy-text').boundingClientRect((data: any) => {
height.value = data.height;
}).exec();
})
}
</script>
<style lang="scss" scoped>
</style>

15
src/app/components/fixed/demo-index/index.vue

@ -1,15 +0,0 @@
<template>
<view>
固定模板示例我也可以装修
<!-- 自定义模板渲染 -->
<diy-group :data="props.data" :pullDownRefreshCount="props.pullDownRefreshCount"></diy-group>
</view>
</template>
<script setup lang="ts">
import { computed, watch } from 'vue';
import diyGroup from '@/addon/components/diy/group/index.vue'
const props = defineProps(['data', 'pullDownRefreshCount']);
</script>
<style></style>

37
src/app/components/tag/tag.vue

@ -1,37 +0,0 @@
<template>
<view class="tags" :style="{ '--backColor': backColor }">
{{ text }}
</view>
</template>
<script setup lang="ts">
interface propInt {
text: string
backColor?: string
}
withDefaults(defineProps<propInt>(), {
text: '',
backColor: '#4b81fb'
})
</script>
<style lang="scss" scoped>
.tags {
position: relative;
margin: 10rpx 0 20rpx;
padding-left: 26rpx;
font-size: 28rpx;
color: #28211f;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 8rpx;
height: 100%;
background: var(--back-color) !important;
}
}
</style>

6
src/app/locale/en/pages.setting.index.json

@ -1,6 +0,0 @@
{
"personalSettings": "Personal settings",
"switchLang": "Switch language",
"version": "Version",
"logout": "Log out"
}

5
src/app/locale/zh-Hans/pages.article.detail.json

@ -1,5 +0,0 @@
{
"detail": "文章详情",
"abstract": "摘要",
"loadingText": "正在加载"
}

7
src/app/locale/zh-Hans/pages.article.list.json

@ -1,7 +0,0 @@
{
"list": "文章列表",
"noData": "~ 暂无数据 ~",
"all": "全部",
"end": "-- 到底了 --",
"searchPlaceholder": "请输入搜索关键词"
}

10
src/app/locale/zh-Hans/pages.auth.bind.json

@ -1,10 +0,0 @@
{
"bindMobile": "绑定手机号",
"bind": "绑定",
"binding": "绑定中",
"agreeTips": "请阅读并同意",
"pleaceAgree": "请勾选已阅读并同意",
"mobilePlaceholder": "请输入手机号",
"codePlaceholder": "请输入验证码",
"weixinUserAuth": "微信用户一键绑定"
}

14
src/app/locale/zh-Hans/pages.auth.login.json

@ -1,14 +0,0 @@
{
"logining": "登录中",
"usernamePlaceholder": "请输入账号",
"passwordPlaceholder": "请输入密码",
"resetpwd": "忘记密码",
"noAccount": "还没有账号",
"toRegister": "去注册",
"and": "和",
"agreeTips": "登录代表您同意",
"isAgreeTips": "请先阅读并同意协议",
"usernameLogin": "密码登录",
"mobileLogin": "验证码登录",
"mobilePlaceholder": "请输入手机号"
}

16
src/app/locale/zh-Hans/pages.auth.register.json

@ -1,16 +0,0 @@
{
"registering": "注册中",
"usernamePlaceholder": "请输入账号",
"passwordPlaceholder": "请输入密码",
"confirmPasswordPlaceholder": "请再次确认密码",
"confirmPasswordError": "两次输入的密码不一致",
"resetpwd": "忘记密码",
"haveAccount": "已有账号",
"toLogin": "去登录",
"and": "和",
"registerAgreeTips": "注册代表您同意",
"isAgreeTips": "请先阅读并同意协议",
"usernameRegister": "账号注册",
"mobileRegister": "手机号注册",
"mobilePlaceholder": "请输入手机号"
}

6
src/app/locale/zh-Hans/pages.auth.resetpwd.json

@ -1,6 +0,0 @@
{
"findPassword": "找回密码",
"passwordPlaceholder": "请输入密码",
"confirmPasswordPlaceholder": "请再次确认密码",
"confirmPasswordError": "两次输入的密码不一致"
}

10
src/app/locale/zh-Hans/pages.index.develop.json

@ -1,10 +0,0 @@
{
"developTitle":"开发环境配置",
"baseUrl":"API请求地址",
"imgUrl":"图片服务器地址",
"siteId":"站点ID(VITE_SITE_ID)",
"siteIdPlaceholder": "请输入站点ID",
"pleaseEnterNumber":"请输入数字",
"maximumCannotExceed":"最大不能超过"
}

7
src/app/locale/zh-Hans/pages.member.account.json

@ -1,7 +0,0 @@
{
"alipayAccountNo": "支付宝账号",
"addBankCard": "添加银行卡",
"addAlipayAccount": "添加支付宝账号",
"endNumber": "尾号",
"bankCard": "银行卡"
}

17
src/app/locale/zh-Hans/pages.member.account_edit.json

@ -1,17 +0,0 @@
{
"addBankCard": "添加银行卡",
"addBankCardTips": "请添加持卡人本人的银行卡",
"addAlipayAccount": "添加支付宝账号",
"addAlipayAccountTips": "请添加已实名的支付宝账号",
"bankRealname": "持卡人姓名",
"bankRealnamePlaceholder": "请输入持卡人姓名",
"bankName": "银行名称",
"bankNamePlaceholder": "请输入银行名称",
"bankAccountNo": "银行卡号",
"bankAccountNoPlaceholder": "请输入银行卡号",
"alipayRealname": "真实姓名",
"alipayRealnamePlaceholder": "请输入真实姓名",
"alipayAccountNo": "支付宝账号",
"alipayAccountNoPlaceholder": "请输入支付宝账号",
"deleteConfirm": "确定要删除该账号吗?"
}

6
src/app/locale/zh-Hans/pages.member.address.json

@ -1,6 +0,0 @@
{
"address": "快递地址",
"locationAddress": "同城配送地址",
"createAddress": "新建收货地址",
"default": "默认"
}

12
src/app/locale/zh-Hans/pages.member.address_edit.json

@ -1,12 +0,0 @@
{
"name": "收货人",
"namePlaceholder": "请输入收货人姓名",
"mobile": "手机号码",
"mobilePlaceholder": "请输入手机号码",
"selectArea":"选择地区",
"selectAreaPlaceholder":"请选择地区",
"address": "详细地址",
"addressPlaceholder": "请填写详细地址",
"defaultAddress": "设为默认地址",
"selectAddressPlaceholder":"请选择地址"
}

30
src/app/locale/zh-Hans/pages.member.apply_cash_out.json

@ -1,30 +0,0 @@
{
"cashOutNow": "立即提现",
"balanceDetail": "余额明细",
"cashOutTo": "提现到",
"cashOutTypePlaceholder": "请选择提现方式",
"wechatpay": "微信默认钱包",
"cashOutMoneyTip": "提现金额",
"money": "可提现余额",
"allTx": "全部提现",
"minWithdrawal": "最小提现金额为",
"commissionTo": "手续费为",
"cashOutList": "提现记录",
"cashOutToWechat": "提现至微信",
"cashOutToWechatTips": "提现至微信零钱",
"cashOutToAlipay": "提现至支付宝",
"cashOutToAlipayTips": "请先添加支付宝账号",
"cashOutToBank": "提现至银行卡",
"cashOutToBankTips": "请先添加银行卡",
"alipayAccountNo": "支付宝账号",
"debitCard": "储蓄卡",
"abnormalOperation": "异常操作",
"noAvailableCashOutType": "没有可用的提现方式",
"applyMoneyPlaceholder": "请输入提现金额",
"moneyformatError": "提现金额格式错误",
"applyMoneyExceed": "提现金额超出可提现金额",
"applyMoneyBelow": "提现金额小于最低提现金额",
"replace": "更换",
"isOpenApply": "提现设置未开启",
"toAdd": "添加"
}

15
src/app/locale/zh-Hans/pages.member.balance.json

@ -1,15 +0,0 @@
{
"balanceInfo": "我的余额",
"recharge": "充值",
"cashOut":"提现",
"balanceDetail": "余额明细",
"accountBalance":"账户余额 (元)",
"balance":"余额 (元)",
"money":"可提现余额 (元)",
"availableBalance": "可用余额",
"rechargeAmountError": "充值金额错误",
"clickRecharge": "立即充值",
"rechargeAmountPlaceholder": "请输入充值金额",
"yuan":"元",
"rechargeRecord":"充值记录"
}

11
src/app/locale/zh-Hans/pages.member.cash_out.json

@ -1,11 +0,0 @@
{
"applyTime": "申请时间",
"toBeReviewed": "官方正在审核,请耐心等待",
"toBeTransfer": "官方正在转账,请耐心等待",
"transfer": "官方已转账,请及时查收",
"cancelApply": "申请已取消",
"balanceDetail": "余额记录",
"commissionDetail": "佣金记录",
"emptyTip": "暂无提现记录",
"commissemptyTip": "暂无佣金记录"
}

12
src/app/locale/zh-Hans/pages.member.cash_out_detail.json

@ -1,12 +0,0 @@
{
"statusName": "当前状态",
"cashOutNo": "交易号",
"serviceMoney": "手续费",
"createTime": "申请时间",
"auditTime": "审核时间",
"transferBank": "银行名称",
"transferAccount": "收款账号",
"refuseReason": "拒绝理由",
"transferTypeName": "转账方式名称",
"transferTime": "转账时间"
}

15
src/app/locale/zh-Hans/pages.member.commission.json

@ -1,15 +0,0 @@
{
"recharge": "充值",
"cashOut":"提现",
"transferMoney":"提现",
"commissionDetail": "佣金明细",
"accountCommission":"当前佣金(元)",
"commission":"累计佣金(元)",
"money":"提现中佣金(元)",
"availableCommission": "可用佣金",
"rechargeAmountError": "充值金额错误",
"clickRecharge": "立即充值",
"rechargeAmountPlaceholder": "请输入充值金额",
"yuan":"元",
"commissionInfo": "我的佣金"
}

7
src/app/locale/zh-Hans/pages.member.detailed_account.json

@ -1,7 +0,0 @@
{
"balanceDetail": "余额明细",
"commissionDetail": "佣金明细",
"balanceEmptyTip": "暂无余额明细",
"moneyEmptyTip": "暂无提现明细",
"commissionEmptyTip": "暂无佣金明细"
}

14
src/app/locale/zh-Hans/pages.member.location_address_edit.json

@ -1,14 +0,0 @@
{
"name": "收货人",
"namePlaceholder": "请输入收货人姓名",
"mobile": "手机号码",
"mobilePlaceholder": "请输入手机号码",
"deliveryAddress":"收货地址",
"selectAddress":"选择地址",
"selectAddressPlaceholder":"请选择地址",
"address": "楼号门牌",
"addressPlaceholder": "详细地址如 1单元101",
"addressError": "请填写门牌号",
"defaultAddress": "设为默认地址",
"update": "修改"
}

12
src/app/locale/zh-Hans/pages.member.personal.json

@ -1,12 +0,0 @@
{
"nickname": "昵称",
"sex": "性别",
"mobile": "手机号",
"birthday": "生日",
"unknown": "未知",
"updateHeadimg": "更换头像",
"updateNickname": "修改昵称",
"man": "男",
"woman": "女",
"bindMobile": "绑定手机"
}

4
src/app/locale/zh-Hans/pages.member.recharge_record.json

@ -1,4 +0,0 @@
{
"rechargeRecord": "充值记录",
"emptyTip": "暂无充值记录"
}

4
src/app/locale/zh-Hans/pages.member.recharge_record_detail.json

@ -1,4 +0,0 @@
{
"orderNo": "订单编号",
"createTime": "创建时间"
}

13
src/app/locale/zh-Hans/pages.member.withdrawal_detail.json

@ -1,13 +0,0 @@
{
"recharge": "充值",
"cashOut":"提现",
"balanceDetail": "余额明细",
"accountBalance":"账户余额(元)",
"balance":"余额(元)",
"money":"可提现余额(元)",
"availableBalance": "可用余额",
"rechargeAmountError": "充值金额错误",
"clickRecharge": "立即充值",
"rechargeAmountPlaceholder": "请输入充值金额",
"yuan":"元"
}

6
src/app/locale/zh-Hans/pages.setting.index.json

@ -1,6 +0,0 @@
{
"personalSettings": "个人设置",
"switchLang": "切换语言",
"version": "版本号",
"logout": "退出登录"
}

25
src/app/pages/auth/agreement.vue

@ -1,25 +0,0 @@
<template>
<view v-if="agreement" class="" style="padding: 30rpx;" :style="themeColor()">
<u-parse :content="agreement.content" :tagStyle="{img: 'vertical-align: top;'}"></u-parse>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getAgreementInfo } from '@/app/api/system'
const agreement = ref(null)
onLoad((option)=> {
getAgreementInfo(option.key).then((res: AnyObject) => {
agreement.value = res.data
uni.setNavigationBarTitle({
title: res.data.title
})
})
})
</script>
<style lang="scss">
</style>

205
src/app/pages/auth/bind.vue

@ -1,205 +0,0 @@
<template>
<view class="w-screen h-screen flex flex-col" :style="themeColor()">
<view class="flex-1">
<!-- #ifdef H5 -->
<view class="" style="height: 100rpx;"></view>
<!-- #endif -->
<view class="" style="margin-bottom: 100rpx;padding-top: 100rpx;">
<view class="font-bold text-lg">{{ t('bindMobile') }}</view>
</view>
<view class="">
<u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef">
<u-form-item label="" prop="mobile" :border-bottom="true">
<u-input v-model="formData.mobile" border="none" clearable :placeholder="t('mobilePlaceholder')" class="!bg-transparent" :disabled="real_name_input"/>
</u-form-item>
<view style="margin-top: 40rpx;">
<u-form-item label="" prop="mobile_code" :border-bottom="true">
<u-input v-model="formData.mobile_code" border="none" clearable :placeholder="t('codePlaceholder')" class="!bg-transparent" :disabled="real_name_input">
<template #suffix>
<sms-code :mobile="formData.mobile" type="bind_mobile" v-model="formData.mobile_key"></sms-code>
</template>
</u-input>
</u-form-item>
</view>
<view class="flex items-start" style="margin-top: 30rpx;" v-if="!info && config.agreement_show">
<u-checkbox-group>
<u-checkbox activeColor="var(--primary-color)" :checked="isAgree" shape="shape" size="14" @change="agreeChange" :customStyle="{'marginTop': '4rpx'}" />
</u-checkbox-group>
<view class="text-xs text-gray-400 flex flex-wrap">
{{ t('agreeTips') }}
<view @click="redirect({ url: '/app/pages/auth/agreement?key=service' })">
<text class="text-primary">{{ t('userAgreement') }}</text>
</view>
<view @click="redirect({ url: '/app/pages/auth/agreement?key=privacy' })">
<text class="text-primary">{{ t('privacyAgreement') }}</text>
</view>
</view>
</view>
<view class="" style="margin-top: 60rpx;">
<u-button type="primary" :text="t('bind')" :loading="loading" :loadingText="t('binding')" @click="handleBind"></u-button>
</view>
<!-- #ifdef MP-WEIXIN -->
<view class="" style="margin-top: 30rpx;">
<u-button type="primary" :plain="true" :text="t('weixinUserAuth')" @click="agreeTips" v-if="!info && config.agreement_show && !isAgree"></u-button>
<u-button type="primary" :plain="true" :text="t('weixinUserAuth')" open-type="getPhoneNumber" @getphonenumber="mobileAuth" v-else></u-button>
</view>
<!-- #endif -->
</u-form>
</view>
</view>
<!-- #ifdef MP-WEIXIN -->
<!-- 小程序隐私协议 -->
<wx-privacy-popup ref="wxPrivacyPopup"></wx-privacy-popup>
<!-- #endif -->
</view>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { t } from '@/locale'
import { bind } from '@/app/api/auth'
import { bindMobile } from '@/app/api/member'
import useMemberStore from '@/stores/member'
import useConfigStore from '@/stores/config'
import { useLogin } from '@/hooks/useLogin'
import { redirect } from '@/utils/common'
const memberStore = useMemberStore()
const info = computed(() => memberStore.info)
const config = computed(() => {
return useConfigStore().login
})
const loading = ref(false)
const isAgree = ref(false)
const formData = reactive({
mobile: '',
mobile_code: '',
mobile_key: ''
})
let real_name_input = ref(true);
onMounted(() => {
//
setTimeout(()=>{
real_name_input.value = false;
},800)
});
uni.getStorageSync('openid') && (Object.assign(formData, { openid: uni.getStorageSync('openid') }))
uni.getStorageSync('pid') && (Object.assign(formData, { pid: uni.getStorageSync('pid') }))
uni.getStorageSync('unionid') && (Object.assign(formData, { unionid: uni.getStorageSync('unionid') }))
const rules = {
'mobile': [
{
type: 'string',
required: true,
message: t('mobilePlaceholder'),
trigger: ['blur', 'change'],
},
{
validator(rule, value, callback) {
let mobile = /^1[3-9]\d{9}$/;
if (!mobile.test(value)){
callback(new Error('请输入正确的手机号'))
} else {
callback()
}
},
message: t('mobileError'),
trigger: ['change', 'blur'],
}
],
'mobile_code': {
type: 'string',
required: true,
message: t('codePlaceholder'),
trigger: ['blur', 'change']
}
}
const agreeChange = () => {
isAgree.value = !isAgree.value
}
const agreeTips = () => {
uni.showToast({ title: `${t('pleaceAgree')}${t('userAgreement')}》《${t('privacyAgreement')}`, icon: 'none' })
}
const formRef = ref(null)
const handleBind = () => {
formRef.value.validate().then(() => {
if (loading.value) return
loading.value = true
const request = info.value ? bindMobile : bind
request(formData).then((res) => {
if (info.value) {
memberStore.getMemberInfo()
redirect({ url: '/app/pages/member/personal', mode: 'redirectTo' })
} else {
memberStore.setToken(res.data.token)
useLogin().handleLoginBack()
}
}).catch(() => {
loading.value = false
})
})
}
const mobileAuth = (e) => {
if (!isAgree.value && !info.value && config.value.agreement_show) {
uni.showToast({
title: `${ t('pleaceAgree') }${ t('userAgreement') }》《${ t('privacyAgreement') }`,
icon: 'none'
})
return
}
if (e.detail.errMsg == 'getPhoneNumber:ok') {
uni.showLoading({ title: '' })
const request = info.value ? bindMobile : bind
request({
openid: formData.openid,
mobile_code: e.detail.code
}).then((res) => {
uni.hideLoading()
if (info.value) {
memberStore.getMemberInfo()
redirect({ url: '/app/pages/member/personal', mode: 'redirectTo' })
} else {
memberStore.setToken(res.data.token)
useLogin().handleLoginBack()
}
}).catch((res) => {
setTimeout(() => {
uni.hideLoading()
}, 2000);
})
}
if (e.detail.errno == 104) {
let msg = '用户未授权隐私权限';
uni.showToast({ title: msg, icon: 'none' })
}
if (e.detail.errMsg == "getPhoneNumber:fail user deny") {
let msg = '用户拒绝获取手机号码';
uni.showToast({ title: msg, icon: 'none' })
}
}
</script>
<style lang="scss">
.u-input{
background-color: transparent !important;
}
</style>

184
src/app/pages/auth/login.vue

@ -1,184 +0,0 @@
<template>
<view class="w-screen h-screen flex flex-col" :style="themeColor()">
<view class="flex-1">
<!-- #ifdef H5 -->
<view style="height: 100rpx;"></view>
<!-- #endif -->
<view class="" style="padding-top: 100rpx;margin-bottom: 100rpx;">
<view class="font-bold text-xl">{{ t('login') }}</view>
</view>
<view class=" text-sm flex font-bold leading-none" style="" v-if="loginType.length > 1">
<block v-for="(item, index) in loginType">
<view :class="{'text-gray-300' : item.type != type}" @click="type = item.type">{{ item.title }}</view>
<view class=" border-solid border-gray-300" style="border-radius: 2px;max-width: 30rpx;" v-show="index == 0"></view>
</block>
</view>
<view class="">
<u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef">
<view v-show="type == 'username'">
<u-form-item label="" prop="username" :border-bottom="true">
<u-input v-model="formData.username" border="none" clearable :placeholder="t('usernamePlaceholder')" autocomplete="off" class="!bg-transparent" :disabled="real_name_input"/>
</u-form-item>
<view class="" style="margin-top: 40rpx;">
<u-form-item label="" prop="password" :border-bottom="true">
<u-input v-model="formData.password" border="none" type="password" clearable :placeholder="t('passwordPlaceholder')" autocomplete="new-password" class="!bg-transparent" :disabled="real_name_input"/>
</u-form-item>
</view>
</view>
<view v-show="type == 'mobile'">
<u-form-item label="" prop="mobile" :border-bottom="true">
<u-input v-model="formData.mobile" border="none" clearable :placeholder="t('mobilePlaceholder')" autocomplete="off" class="!bg-transparent" :disabled="real_name_input"/>
</u-form-item>
<view class="" style="margin-top: 40rpx;">
<u-form-item label="" prop="mobile_code" :border-bottom="true">
<u-input v-model="formData.mobile_code" border="none" clearable class="!bg-transparent" :disabled="real_name_input" :placeholder="t('codePlaceholder')">
<template #suffix>
<sms-code :mobile="formData.mobile" type="login" v-model="formData.mobile_key"></sms-code>
</template>
</u-input>
</u-form-item>
</view>
</view>
<view class="flex text-xs justify-between text-gray-400" style="margin-top: 40rpx;">
<view @click="redirect({ url: '/app/pages/auth/register' })">{{ t('noAccount') }}
<text class="text-primary">{{ t('toRegister') }}</text>
</view>
<view @click="redirect({ url: '/app/pages/auth/resetpwd' })">{{ t('resetpwd') }}</view>
</view>
<view class="" style="margin-top: 80rpx;">
<u-button type="primary" :text="t('login')" :loading="loading" :loadingText="t('logining')" @click="handleLogin">
</u-button>
</view>
</u-form>
</view>
</view>
<view class="text-xs flex justify-center w-full" style="" v-if="configStore.login.agreement_show">
<text class="iconfont " style="font-size: 34rpx;margin-right: 12rpx;" :class="isAgree ? 'iconxuanze1' : 'nc-iconfont nc-icon-yuanquanV6xx'" @click="isAgree = !isAgree"></text>
{{ t('agreeTips') }}
<view @click="redirect({ url: '/app/pages/auth/agreement?key=service' })">
<text class="text-primary">{{ t('userAgreement') }}</text>
</view>
{{ t('and') }}
<view @click="redirect({ url: '/app/pages/auth/agreement?key=privacy' })">
<text class="text-primary">{{ t('privacyAgreement') }}</text>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { usernameLogin, mobileLogin } from '@/app/api/auth'
import useMemberStore from '@/stores/member'
import useConfigStore from '@/stores/config'
import { useLogin } from '@/hooks/useLogin'
import { t } from '@/locale'
import { redirect } from '@/utils/common'
const formData = reactive({
username: '',
password: '',
mobile: '',
mobile_code: '',
mobile_key: ''
})
let real_name_input = ref(true);
onMounted(() => {
//
setTimeout(()=>{
real_name_input.value = false;
},800)
});
if (!uni.getStorageSync('autoLoginLock')) {
uni.getStorageSync('openid') && (Object.assign(formData, { openid: uni.getStorageSync('openid') }))
uni.getStorageSync('unionid') && (Object.assign(formData, { unionid: uni.getStorageSync('unionid') }))
}
uni.getStorageSync('pid') && (Object.assign(formData, { pid: uni.getStorageSync('pid') }))
const memberStore = useMemberStore()
const configStore = useConfigStore()
const loading = ref(false)
const type = ref('')
const loginType = computed(() => {
const value = []
configStore.login.is_username && (value.push({ type: 'username', title: t('usernameLogin') }))
configStore.login.is_mobile && (value.push({ type: 'mobile', title: t('mobileLogin') }))
type.value = value[0] ? value[0].type : ''
return value
})
const rules = computed(() => {
return {
'username': {
type: 'string',
required: type.value == 'username',
message: t('usernamePlaceholder'),
trigger: ['blur', 'change'],
},
'password': {
type: 'string',
required: type.value == 'username',
message: t('passwordPlaceholder'),
trigger: ['blur', 'change']
},
'mobile': [
{
type: 'string',
required: type.value == 'mobile',
message: t('mobilePlaceholder'),
trigger: ['blur', 'change'],
},
{
validator(rule, value) {
if (type.value != 'mobile') return true
else return uni.$u.test.mobile(value)
},
message: t('mobileError'),
trigger: ['change', 'blur'],
}
],
'mobile_code': {
type: 'string',
required: type.value == 'mobile',
message: t('codePlaceholder'),
trigger: ['blur', 'change']
}
}
})
const isAgree = ref(false)
const formRef = ref(null)
const handleLogin = () => {
formRef.value.validate().then(() => {
if (configStore.login.agreement_show && !isAgree.value) {
uni.showToast({ title: t('isAgreeTips'), icon: 'none' });
return false;
}
if (loading.value) return
loading.value = true
const login = type.value == 'username' ? usernameLogin : mobileLogin
login(formData).then((res) => {
memberStore.setToken(res.data.token)
useLogin().handleLoginBack()
}).catch(() => {
loading.value = false
})
})
}
</script>
<style lang="scss">
.u-input{
background-color: transparent !important;
}
</style>

229
src/app/pages/auth/register.vue

@ -1,229 +0,0 @@
<template>
<view class="w-screen h-screen flex flex-col" :style="themeColor()">
<view class="flex-1">
<!-- #ifdef H5 -->
<view class="" style="height: 100rpx;"></view>
<!-- #endif -->
<view class="" style="padding-top: 100rpx;margin-bottom: 100rpx;">
<view class="font-bold text-xl">{{ t('register') }}</view>
</view>
<view class=" text-sm flex font-bold leading-none" style="margin-bottom: 50rpx;" v-if="registerType.length > 1">
<block v-for="(item, index) in registerType">
<view :class="{'text-gray-300' : item.type != type}" @click="type = item.type">{{ item.title }}</view>
<view class=" border-solid border-0 border-gray-300" style="border-radius: 2px;" v-show="index == 0"></view>
</block>
</view>
<view class="">
<u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef">
<view v-show="type == 'username'">
<view class="" style="margin-top: 30rpx;">
<u-form-item label="" prop="username" :border-bottom="true">
<u-input v-model="formData.username" border="none" clearable :placeholder="t('usernamePlaceholder')" class="!bg-transparent" :disabled="real_name_input"/>
</u-form-item>
</view>
<view style="margin-top: 30rpx;">
<u-form-item label="" prop="password" :border-bottom="true">
<u-input v-model="formData.password" border="none" type="password" clearable :placeholder="t('passwordPlaceholder')" class="!bg-transparent" :disabled="real_name_input"/>
</u-form-item>
</view>
<view style="margin-top: 30rpx;">
<u-form-item label="" prop="confirm_password" :border-bottom="true">
<u-input v-model="formData.confirm_password" border="none" type="password" clearable :placeholder="t('confirmPasswordPlaceholder')" class="!bg-transparent" :disabled="real_name_input"/>
</u-form-item>
</view>
</view>
<view v-show="type == 'mobile' || configStore.login.is_bind_mobile">
<view style="margin-top: 30rpx;">
<u-form-item label="" prop="mobile" :border-bottom="true">
<u-input v-model="formData.mobile" border="none" clearable :placeholder="t('mobilePlaceholder')" class="!bg-transparent" :disabled="real_name_input"/>
</u-form-item>
</view>
<view style="margin-top: 30rpx;">
<u-form-item label="" prop="code" :border-bottom="true">
<u-input v-model="formData.mobile_code" border="none" clearable :placeholder="t('codePlaceholder')" class="!bg-transparent" :disabled="real_name_input">
<template #suffix>
<sms-code :mobile="formData.mobile" type="register" v-model="formData.mobile_key"></sms-code>
</template>
</u-input>
</u-form-item>
</view>
</view>
<view v-show="type == 'username'">
<view style="margin-top: 30rpx;">
<u-form-item label="" prop="captcha_code" :border-bottom="true">
<u-input v-model="formData.captcha_code" border="none" clearable :placeholder="t('captchaPlaceholder')" class="!bg-transparent" :disabled="real_name_input">
<template #suffix>
<image :src="captcha.image.value" class="" style="height: 48rpx;margin-left: 20rpx;" mode="heightFix" @click="captcha.refresh()"></image>
</template>
</u-input>
</u-form-item>
</view>
</view>
<view class="flex text-xs justify-between text-gray-400" style="margin-top: 20rpx;">
<view @click="redirect({ url: '/app/pages/auth/login' })">{{ t('haveAccount') }}<text class="text-primary">{{ t('toLogin') }}</text></view>
</view>
<view class="" style="margin-top: 80rpx;">
<u-button type="primary" :text="t('register')" :loading="loading" :loadingText="t('registering')" @click="handleRegister">
</u-button>
</view>
</u-form>
</view>
</view>
<view class="text-xs flex justify-center w-full" style="" v-if="configStore.login.agreement_show">
<text class="iconfont" style="font-style: 34rpx;margin-right: 12rpx;" :class="isAgree ? 'iconxuanze1' : 'nc-iconfont nc-icon-yuanquanV6xx'" @click="isAgree = !isAgree"></text>
{{ t('registerAgreeTips') }}
<view @click="redirect({ url: '/app/pages/auth/agreement?key=service' })">
<text class="text-primary">{{ t('userAgreement') }}</text>
</view>
{{ t('and') }}
<view @click="redirect({ url: '/app/pages/auth/agreement?key=privacy' })">
<text class="text-primary">{{ t('privacyAgreement') }}</text>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { usernameRegister, mobileRegister } from '@/app/api/auth'
import useMemberStore from '@/stores/member'
import useConfigStore from '@/stores/config'
import { useLogin } from '@/hooks/useLogin'
import { useCaptcha } from '@/hooks/useCaptcha'
import { t } from '@/locale'
import { redirect } from '@/utils/common'
const formData = reactive({
username: '',
password: '',
confirm_password: '',
mobile: '',
mobile_code: '',
mobile_key: '',
captcha_key: '',
captcha_code: ''
})
let real_name_input = ref(true);
onMounted(() => {
//
setTimeout(()=>{
real_name_input.value = false;
},800)
});
if (!uni.getStorageSync('autoLoginLock')) {
uni.getStorageSync('openid') && (Object.assign(formData, {openid: uni.getStorageSync('openid')}))
uni.getStorageSync('pid') && (Object.assign(formData, {pid: uni.getStorageSync('pid')}))
}
uni.getStorageSync('unionid') && (Object.assign(formData, { unionid: uni.getStorageSync('unionid') }))
const captcha = useCaptcha(formData)
captcha.refresh()
const memberStore = useMemberStore()
const configStore = useConfigStore()
const loading = ref(false)
const type = ref('')
const registerType = computed(()=> {
const value = []
configStore.login.is_username && (value.push({ type: 'username', title: t('usernameRegister') }))
configStore.login.is_mobile && !configStore.login.is_bind_mobile && (value.push({ type: 'mobile', title: t('mobileRegister') }))
type.value = value[0] ? value[0].type : ''
return value
})
const rules = computed(()=>{
return {
'username': {
type: 'string',
required: type.value == 'username',
message: t('usernamePlaceholder'),
trigger: ['blur', 'change'],
},
'password': {
type: 'string',
required: type.value == 'username',
message: t('passwordPlaceholder'),
trigger: ['blur', 'change']
},
'confirm_password': [
{
type: 'string',
required: type.value == 'username',
message: t('confirmPasswordPlaceholder'),
trigger: ['blur', 'change']
},
{
validator(rule, value){
return value == formData.password
},
message: t('confirmPasswordError'),
trigger: ['change','blur'],
}
],
'mobile': [
{
type: 'string',
required: type.value == 'mobile' || configStore.login.is_bind_mobile,
message: t('mobilePlaceholder'),
trigger: ['blur', 'change'],
},
{
validator(rule, value){
if (type.value != 'mobile' && !configStore.login.is_bind_mobile) return true
else return uni.$u.test.mobile(value)
},
message: t('mobileError'),
trigger: ['change','blur'],
}
],
'mobile_code': {
type: 'string',
required: type.value == 'mobile' || configStore.login.is_bind_mobile,
message: t('codePlaceholder'),
trigger: ['blur', 'change']
},
'captcha_code': {
type: 'string',
required: type.value == 'username',
message: t('captchaPlaceholder'),
trigger: ['blur', 'change'],
}
}
})
const isAgree = ref(false)
const formRef = ref(null)
const handleRegister = () => {
formRef.value.validate().then(() => {
if (configStore.login.agreement_show && !isAgree.value) {
uni.showToast({ title: t('isAgreeTips'), icon: 'none' });
return false;
}
if (loading.value) return
loading.value = true
const register = type.value == 'username' ? usernameRegister : mobileRegister
register(formData).then((res: responseResult) => {
memberStore.setToken(res.data.token)
useLogin().handleLoginBack()
}).catch(() => {
loading.value = false
captcha.refresh()
})
})
}
</script>
<style lang="scss">
.u-input{
background-color: transparent !important;
}
</style>

135
src/app/pages/auth/resetpwd.vue

@ -1,135 +0,0 @@
<template>
<view class="w-screen h-screen flex flex-col" :style="themeColor()">
<view class="flex-1">
<!-- #ifdef H5 -->
<view class="" style="100rpx"></view>
<!-- #endif -->
<view class="" style="padding-top: 100rpx;margin-bottom: 100rpx;">
<view class="font-bold text-xl">{{ t('findPassword') }}</view>
</view>
<view class="">
<u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef">
<view style="margin-top: 30rpx;">
<u-form-item label="" prop="mobile" :border-bottom="true">
<u-input v-model="formData.mobile" border="none" clearable :placeholder="t('mobilePlaceholder')" class="!bg-transparent" :disabled="real_name_input"/>
</u-form-item>
</view>
<view style="margin-top: 30rpx;">
<u-form-item label="" prop="code" :border-bottom="true">
<u-input v-model="formData.mobile_code" border="none" clearable :placeholder="t('codePlaceholder')" class="!bg-transparent" :disabled="real_name_input">
<template #suffix>
<sms-code :mobile="formData.mobile" type="find_pass" v-model="formData.mobile_key"></sms-code>
</template>
</u-input>
</u-form-item>
</view>
<view style="margin-top: 30rpx;">
<u-form-item label="" prop="password" :border-bottom="true">
<u-input v-model="formData.password" border="none" type="password" clearable :placeholder="t('passwordPlaceholder')" class="!bg-transparent" :disabled="real_name_input"/>
</u-form-item>
</view>
<view style="margin-top: 30rpx;">
<u-form-item label="" prop="confirm_password" :border-bottom="true">
<u-input v-model="formData.confirm_password" border="none" type="password" clearable :placeholder="t('confirmPasswordPlaceholder')" class="!bg-transparent" :disabled="real_name_input"/>
</u-form-item>
</view>
<view class="" style="margin-top: 80rpx;">
<u-button type="primary" :text="t('confirm')" :loading="loading" :loadingText="t('confirm')" @click="handleConfirm">
</u-button>
</view>
</u-form>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { t } from '@/locale'
import { resetPassword } from '@/app/api/system'
import { redirect } from '@/utils/common'
const formData = reactive({
mobile: '',
mobile_code: '',
mobile_key: '',
password: '',
confirm_password: ''
})
let real_name_input = ref(true);
onMounted(() => {
//
setTimeout(()=>{
real_name_input.value = false;
},800)
});
const loading = ref(false)
const formRef = ref(null)
const rules = {
'password': {
type: 'string',
required: true,
message: t('passwordPlaceholder'),
trigger: ['blur', 'change']
},
'confirm_password': [
{
type: 'string',
required: true,
message: t('confirmPasswordPlaceholder'),
trigger: ['blur', 'change']
},
{
validator(rule, value) {
return value == formData.password
},
message: t('confirmPasswordError'),
trigger: ['change', 'blur'],
}
],
'mobile': [
{
type: 'string',
required: true,
message: t('mobilePlaceholder'),
trigger: ['blur', 'change'],
},
{
validator(rule, value) {
return uni.$u.test.mobile(value)
},
message: t('mobileError'),
trigger: ['change', 'blur'],
}
],
'mobile_code': {
type: 'string',
required: true,
message: t('codePlaceholder'),
trigger: ['blur', 'change']
}
}
const handleConfirm = () => {
formRef.value.validate().then(() => {
if (loading.value) return
loading.value = true
resetPassword(formData).then((res : responseResult) => {
redirect({ url: '/app/pages/auth/login', mode: 'redirectTo' })
}).catch(() => {
loading.value = false
})
})
}
</script>
<style lang="scss">
.u-input{
background-color: transparent !important;
}
</style>

BIN
src/app/pages/commodity/commodity.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 B

156
src/app/pages/commodity/index.vue

@ -1,156 +0,0 @@
<script setup>
import { onLoad } from '@dcloudio/uni-app';
import { computed, ref } from 'vue';
import { getProductDetails, orderCalculation, orderCreation } from '@/app/api/home';
const imgUrl = import.meta.env.VITE_IMG_DOMAIN;
const details = ref({});
const id = ref()
const getDetails = async () => {
const res = await getProductDetails({ goods_id: id.value });
console.log(res);
details.value = res.data;
uni.setNavigationBarTitle({
title: details.value.goods.goods_name
});
};
const buy = async () => {
//
const res = await orderCalculation({
sku_data: [ {
sku_id: details.value.sku_id,
num: 1
} ]
});
if (!res) {
return uni.showToast({
title: '购买失败'
});
}
console.log(res);
const res2 = await orderCreation({
order_key: res.data.order_key,
member_remark: ''
});
if (!res2) {
return uni.showToast({
title: '购买失败'
});
}
uni.navigateTo({
url: '../../../addon/mine/orderList/orderList?barName=服务订单&is_shop=1',
});
};
onLoad(async (o) => {
id.value = o.id
uni.showLoading()
await getDetails();+
uni.hideLoading()
});
</script>
<template>
<div class="commodity">
<img :src="imgUrl + 'addon/system/backage_3.png'" class="commodity-cover" alt=""/>
<image mode="aspectFill" :src="details.goods.goods_cover" class="commodity-cover" alt=""/>
<div class="commodity-card">
<p class="commodity-card-title">{{ details.goods.goods_name }}</p>
<p class="commodity-card-description" v-html="details.goods.goods_desc"></p>
</div>
<div class="commodity-footer">
<div class="commodity-footer-price">
<div class="commodity-footer-price-iconContainer">
<img class="commodity-footer-price-iconContainer-img" src="./commodity.png" alt=""/>
</div>
{{ details.price }}
</div>
<up-button @click="buy" style="display: inline-flex" shape="circle" color="#4B81FB"
text="立即购买"></up-button>
</div>
</div>
</template>
<style scoped lang="scss">
.commodity {
width: 100%;
height: 100vh;
position: relative;
background: #F5F8F7;
display: flex;
flex-direction: column;
justify-content: space-between;
&-cover {
width: 100%;
height: 30vh;
object-fit: contain;
}
&-card {
width: 90%;
height: 60vh;
background: white;
border-radius: 10px;
margin: -10vh auto 0 auto;
position: relative;
z-index: 1;
padding: 20px;
box-sizing: border-box;
overflow: auto;
&-title {
font-size: 20px;
margin-bottom: 10px;
font-weight: bold;
}
&-description {
font-size: 16px;
line-height: 30px;
text-align: justify;
}
}
&-footer {
background: white;
height: 10vh;
display: flex;
padding: 20px 30px;
box-sizing: border-box;
justify-content: space-between;
align-items: center;
gap: 20px;
&-price {
display: flex;
align-items: center;
font-size: 20px;
gap: 10px;
&-iconContainer {
width: 35px;
height: 35px;
border: 1px solid #86A8FF;
display: flex;
justify-content: center;
align-items: center;
background: rgba(75, 129, 251, 0.1);
border-radius: 100%;
&-img {
width: 20px;
height: 20px;
}
}
}
}
}
</style>

14
src/app/pages/electionResults/index.vue

@ -1,14 +0,0 @@
//
<template>
</template>
<script lang="ts" setup>
import { onLoad, onShow } from '@dcloudio/uni-app';
import { reactive, ref } from 'vue';
</script>
<style lang="scss" scoped>
</style>

12
src/app/pages/mine/index.vue

@ -1,12 +0,0 @@
//
<template>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
</script>
<style></style>

14
src/app/pages/myElection/index.vue

@ -1,14 +0,0 @@
//
<template>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
</script>
<style lang="scss" scoped>
</style>

368
src/app/pages/votingElection/index.vue

@ -1,368 +0,0 @@
<script setup lang="ts">
import { onShow } from '@dcloudio/uni-app';
import { log } from 'console';
import {
onMounted,
ref
} from 'vue';
import PullToRefresh from '@/components/PullToRefresh.vue';
const tpList = ref([
{
img: '/static/img/grxx.png',
name: '陈志远',
class: '现任财务总监',
}
])
const doSearch = (_formData : { page : number; limit : number }, onSuccess : Function) => {
// const submitData = { ...formData }
// listApi(submitData, userStore.useType).then((res) => {
// const { data } = res as { data: { data: any; total: number } }
// onSuccess({ data })
// })
onSuccess({
data: {
data: [
{
img: '/static/img/grxx.png',
name: '陈志远',
class: '现任财务总监',
},
{
img: '/static/img/grxx.png',
name: '陈志远',
class: '现任财务总监',
},
{
img: '/static/img/grxx.png',
name: '陈志远',
class: '现任财务总监',
},
{
img: '/static/img/grxx.png',
name: '陈志远',
class: '现任财务总监',
},
{
img: '/static/img/grxx.png',
name: '陈志远',
class: '现任财务总监',
}
],
total: 0
}
})
}
const selectIndex = ref(0)
const buttlist = ref([
{
butname: '同意'
},
{
butname: '反对'
},
{
butname: '弃权'
}
])
const selectBut = (index : number) => {
selectIndex.value = index
}
const loadData = async () => {
//
console.log(1111111111111)
};
</script>
<template>
<pull-to-refresh :on-refresh="loadData">
<!-- 你的页面内容 -->
<view class="box">
<ex-list ref="reListRef" custom-list-type="scroll" :on-form-search="doSearch" empty-text=""
customListType="custom">
<template v-slot="{ data }">
<view class="headpart">
<view class="title">
2024年度董事会成员选举
</view>
<view class="time">
投票开始时间2024年3月15日 18:00
</view>
<view class="time">
投票截止时间2024年3月15日 18:00
</view>
</view>
<view class="tppart">
<view class="tpone" v-for="(row, index) of data" :key="index">
<view class="topp">
<img style="width: 96rpx;height: 96rpx; border-radius: 50%;" :src="row.img" alt="" />
<view class="rightpart">
<view class="name">
{{ row.name }}
</view>
<view class="class">
{{ row.class }}
</view>
</view>
</view>
<view class="bottomp">
<view class="minbut" v-for="(item,index) in buttlist" :key="index"
@click="selectBut(index)" :class="{active: selectIndex === index}">
{{item.butname}}
</view>
</view>
</view>
</view>
</template>
</ex-list>
<view class="bottbutton">
<view class="qbty">
全部同意
</view>
<view class="tjtp">
提交投票
</view>
</view>
</view>
</pull-to-refresh>
</template>
<style scoped lang="scss">
.box {
width: 100%;
height: 100vh;
background-color: #F9FAFB;
.headpart {
width: 86%;
height: 194rpx;
border-radius: 24rpx;
background-color: #EFF6FF;
margin: 32rpx auto;
padding: 20rpx 3%;
.title {
color: #2563EB;
font-size: 28rpx;
margin-top: 16rpx;
}
.time {
margin-top: 16rpx;
color: #4B5563;
font-size: 28rpx;
}
}
.tppart {
width: 100%;
max-height: 69vh;
overflow-y: auto;
display: grid;
justify-items: center;
.tpone {
width: 91%;
height: 264rpx;
padding: 20rpx 4%;
box-sizing: border-box;
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 -2rpx rgba(0, 0, 0, 0.1), 0rpx 2rpx 6rpx 0rpx rgba(0, 0, 0, 0.1);
margin-top: 32rpx;
display: grid;
.topp {
display: flex;
.rightpart {
margin-left: 32rpx;
display: grid;
justify-items: left;
align-content: baseline;
.name {
font-family: Roboto;
font-size: 28rpx;
font-weight: 500;
line-height: 42rpx;
letter-spacing: normal;
color: #000000;
margin-top: 7rpx;
}
.class {
font-family: Roboto;
font-size: 28rpx;
font-weight: normal;
line-height: 40rpx;
letter-spacing: normal;
color: #6B7280;
margin-top: 7rpx;
}
}
}
.bottomp {
display: flex;
justify-content: space-between;
gap: 0rpx 20rpx;
.minbut {
flex: 1;
width: 190rpx;
height: 76rpx;
/* 自动布局 */
display: flex;
justify-content: center;
padding: 16rpx 32rpx;
flex-wrap: wrap;
align-content: flex-start;
border-radius: 8rpx;
background: #FFFFFF;
box-sizing: border-box;
border: 2rpx solid #D1D5DB;
font-family: Roboto;
font-size: 28rpx;
font-weight: normal;
line-height: 40rpx;
text-align: center;
letter-spacing: normal;
color: #4B5563;
}
.minbut.active {
border: 2rpx solid #2563EB;
color: #2563EB;
}
}
}
.tpone:first-child {
margin-top: 0;
}
}
.bottbutton {
width: 100%;
height: 10vh;
display: flex;
padding: 24rpx 32rpx;
gap: 0rpx 24rpx;
flex-wrap: wrap;
align-content: flex-start;
background: #FFFFFF;
box-sizing: border-box;
border-width: 2rpx 0rpx 0rpx 0rpx;
border-style: solid;
border-color: #F3F4F6;
position: fixed;
bottom: 0;
.qbty {
width: 331rpx;
height: 90rpx;
/* 自动布局 */
display: flex;
box-sizing: border-box;
justify-content: center;
padding: 24rpx 0rpx;
gap: 0rpx 20rpx;
flex-wrap: wrap;
border-radius: 8rpx;
background: #EFF6FF;
font-family: Roboto;
font-size: 28rpx;
font-weight: 500;
line-height: 42rpx;
text-align: center;
letter-spacing: normal;
color: #2563EB;
}
.tjtp {
width: 331rpx;
height: 90rpx;
/* 自动布局 */
display: flex;
box-sizing: border-box;
justify-content: center;
padding: 24rpx 0rpx;
gap: 0rpx 20rpx;
flex-wrap: wrap;
border-radius: 8rpx;
background: #2563EB;
font-family: Roboto;
font-size: 28rpx;
font-weight: 500;
line-height: 42rpx;
text-align: center;
letter-spacing: normal;
color: #FFFFFF;
}
}
.address-items {
padding: 20rpx 15rpx 32rpx 28rpx;
background-color: #fff;
border-bottom: 2rpx solid #f5f5f5;
.left {
flex: 1;
overflow: hidden;
.name {
font-size: 28rpx;
font-weight: 700;
color: #101010;
line-height: 40rpx;
margin-right: 16rpx;
}
.isdefault {
font-size: 20rpx;
font-weight: 400;
color: #fff;
padding: 4rpx 12rpx;
border-radius: 8rpx;
background: linear-gradient(90deg, #4778ff 0%, #4778ffb8 100%);
}
.info {
width: 100%;
color: #666;
font-size: 24rpx;
font-weight: 400;
margin-top: 12rpx;
}
}
.right {
width: 140rpx;
padding-left: 32rpx;
font-size: 24rpx;
font-weight: 400;
color: #4979ff;
text-align: right;
}
}
.buts {
width: 100%;
height: 10vh;
background-color: F3F4F6;
position: fixed;
bottom: 0;
border-top: 2rpx solid #ebebec;
}
}
</style>

33
src/app/pages/webview/index.vue

@ -1,33 +0,0 @@
<template>
<view :style="themeColor()">
<u-icon name="arrow-left" class="navigate-back" @click="navigateBack"></u-icon>
<web-view :src="src"></web-view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref } from 'vue';
const src = ref('')
onLoad((option : any) => {
src.value = decodeURIComponent(option.src);
})
const navigateBack = () => {
uni.navigateBack({
delta: 1
});
}
</script>
<style lang="scss" scoped>
.navigate-back {
position: absolute;
top: 34rpx;
left: 34rpx;
z-index: 999;
font-size: 16px;
}
</style>

138
src/app/stores/diy.ts

@ -1,138 +0,0 @@
import { defineStore } from 'pinia'
import { toRaw } from 'vue'
import { diyRedirect, currRoute, getToken } from '@/utils/common';
import { useLogin } from '@/hooks/useLogin';
interface Diy {
mode: string, // 模式:decorate 装修,为空表示正常
pageMode: string, // 页面展示模式,diy:自定义,fixed:固定
currentIndex: number,
global: {
title: string,
pageStartBgColor: string, // 页面背景颜色(开始)
pageEndBgColor: string, // 页面背景颜色(结束)
bottomTabBarSwitch: boolean, // 底部导航开关
bgUrl: string
},
// 组件集合
value: any[],
topFixedStatus: string, // 置顶组件的状态
scrollTop: number,
topTabarHeight: number
}
const useDiyStore = defineStore('diy', {
state: (): Diy => {
return {
mode: '',
pageMode: 'diy',
currentIndex: -99,
global: {
title: "",
pageStartBgColor: '', // 页面背景颜色(开始)
pageEndBgColor: '', // 页面背景颜色(结束)
bottomTabBarSwitch: true,
bgUrl: ''
},
value: [], // 组件集合
topFixedStatus: 'home', // 顶部 置顶组件状态,home:展示首页数据、diy:展示置顶组件定义的子页面
scrollTop: 0, // 滚动位置
topTabarHeight: 0
}
},
getters: {},
actions: {
// 初始化
init() {
// #ifdef H5
var data = JSON.stringify({
type: 'init',
load: true
});
// 传输给后台数据
window.parent.postMessage(data, '*');
// 监听父页面发来的消息
window.addEventListener('message', event => {
try {
let data = JSON.parse(event.data);
this.currentIndex = data.currentIndex;
this.pageMode = data.pageMode;
if (data.global) this.global = data.global;
if (data.value) this.value = data.value;
if (this.value) {
this.value.forEach((item, index) => {
item.pageStyle = '';
if (item.pageStartBgColor) {
if (item.pageStartBgColor && item.pageEndBgColor) item.pageStyle += `background:linear-gradient(${ item.pageGradientAngle },${ item.pageStartBgColor },${ item.pageEndBgColor });`;
else item.pageStyle += 'background-color:' + item.pageStartBgColor + ';';
}
if (item.margin) {
if (item.margin.top > 0) {
item.pageStyle += 'padding-top:' + item.margin.top * 2 + 'rpx' + ';';
} else {
item.pageStyle += 'padding-top:2rpx' + ';'; // 装修实时预览需要设置
}
item.pageStyle += 'padding-bottom:' + item.margin.bottom * 2 + 'rpx' + ';';
item.pageStyle += 'padding-right:' + item.margin.both * 2 + 'rpx' + ';';
item.pageStyle += 'padding-left:' + item.margin.both * 2 + 'rpx' + ';';
}
});
}
// console.log('uniapp 接受后台装修返回的组件数据', data);
} catch (e) {
console.log('uni-app diy 接受数据错误', e)
}
}, false);
// #endif
},
// 将数据传输给后台
postMessage(index: any, component: any) {
// #ifdef H5
this.currentIndex = index;
if (component)
var data: any = JSON.stringify({
type: 'data',
index: this.currentIndex,
global: toRaw(this.global),
value: toRaw(this.value),
component: toRaw(component)
});
// 传输给后台数据
window.parent.postMessage(data, '*');
// #endif
},
// 选中正在编辑的组件
changeCurrentIndex(index: number, component: any = null) {
// #ifdef H5
// 实际展示禁止编辑
if (this.mode == '') return;
// 减少重复请求
if (this.currentIndex == index) return;
this.currentIndex = index;
var data = JSON.stringify({
type: 'change',
index,
component: toRaw(component)
});
window.parent.postMessage(data, '*');
// #endif
},
toRedirect(data: any) {
if (Object.keys(data).length) {
if (!data.name) return;
if (currRoute() == 'app/pages/member/index' && !getToken()) {
useLogin().setLoginBack({ url: data.url })
return;
}
diyRedirect(data);
}
}
}
})
export default useDiyStore

56
src/components/PullToRefresh.vue

@ -1,56 +0,0 @@
<!-- components/PullToRefresh.vue -->
<template>
<scroll-view
scroll-y
:refresher-enabled="true"
:refresher-triggered="isRefreshing"
@refresherrefresh="handleRefresh"
class="scroll-view"
>
<!-- 下拉刷新提示区域 -->
<view v-if="isRefreshing" class="refresh-loading">
<text>加载中...</text>
</view>
<!-- 内容插槽 -->
<slot />
</scroll-view>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps({
onRefresh: {
type: Function,
required: true
}
});
const isRefreshing = ref(false);
const handleRefresh = async () => {
if (isRefreshing.value) return;
isRefreshing.value = true;
try {
await props.onRefresh();
} finally {
isRefreshing.value = false;
}
};
</script>
<style scoped>
.scroll-view {
height: 100vh;
box-sizing: border-box;
}
.refresh-loading {
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
color: #666;
}
</style>

154
src/components/area-select/area-select.vue

@ -1,154 +0,0 @@
<template>
<u-popup :show="show" @close="show = false" mode="bottom" :round="10" :closeable="true">
<view class="text-center p-[30rpx]">请选择地区</view>
<view class="flex p-[30rpx] text-sm font-semibold">
<view v-if="areaList.province.length" class="flex-1" :class="{'text-[var(--primary-color)]': currSelect == 'province'}" @click="currSelect = 'province'">
<view v-if="selected.province">{{ selected.province.name }}</view>
<view v-else>请选择</view>
</view>
<view v-if="areaList.city.length" class="flex-1" :class="{'text-[var(--primary-color)]': currSelect == 'city' }" @click="currSelect = 'city'">
<view v-if="selected.city">{{ selected.city.name }}</view>
<view v-else>请选择</view>
</view>
<view v-if="areaList.district.length" class="flex-1" :class="{'text-[var(--primary-color)]': currSelect == 'district' }" @click="currSelect = 'district'">
<view v-if="selected.district">{{ selected.district.name }}</view>
<view v-else>请选择</view>
</view>
</view>
<scroll-view scroll-y="true" class="h-[50vh]">
<view class="flex p-[30rpx] pt-0 text-sm">
<view v-if="areaList.province.length" v-show="currSelect == 'province'">
<view v-for="item in areaList.province" class="leading-loose" :class="{'text-[var(--primary-color)]': selected.province && selected.province.id == item.id }"
@click="selected.province = item" >{{ item.name }}</view>
</view>
<view v-if="areaList.city.length" v-show="currSelect == 'city'">
<view v-for="item in areaList.city" class="leading-loose" :class="{'text-[var(--primary-color)]': selected.city && selected.city.id == item.id }"
@click="selected.city = item">{{ item.name }}</view>
</view>
<view v-if="areaList.district.length" v-show="currSelect == 'district'">
<view v-for="item in areaList.district" class="leading-loose" :class="{'text-[var(--primary-color)]': selected.district && selected.district.id == item.id }"
@click="selected.district = item">{{ item.name }}</view>
</view>
</view>
</scroll-view>
</u-popup>
</template>
<script setup lang="ts">
import { ref, reactive, watch } from 'vue'
import { getAreaListByPid, getAreaByCode } from '@/app/api/system'
const prop = defineProps({
areaId: {
type: Number,
default: 0
}
})
const show = ref(false)
const areaList = reactive({
province: [],
city: [],
district: []
})
const currSelect = ref('province')
const selected = reactive({
province: null,
city: null,
district: null
})
getAreaListByPid(0).then(({ data }) => {
areaList.province = data
}).catch()
watch(() => prop.areaId, (nval, oval)=> {
if (nval && !oval) {
getAreaByCode(nval).then(({ data }) => {
data.province && (selected.province = data.province)
data.city && (selected.city = data.city)
data.district && (selected.district = data.district)
})
.catch()
}
},{
immediate:true
})
/**
* 监听省变更
*/
watch(() => selected.province, ()=> {
getAreaListByPid(selected.province.id).then(({ data }) => {
areaList.city = data
currSelect.value = 'city'
if (selected.city) {
let isExist = false
for (let i = 0; i < data.length; i++) {
if (selected.city.id == data[i].id) {
isExist = true
break
}
}
if (!isExist) {
selected.city = null
}
}
}).catch()
}, { deep: true })
/**
* 监听市变更
*/
watch(() => selected.city, (nval)=> {
if (nval) {
getAreaListByPid(selected.city.id).then(({ data }) => {
areaList.district = data
currSelect.value = 'district'
if (selected.district) {
let isExist = false
for (let i = 0; i < data.length; i++) {
if (selected.district.id == data[i].id) {
isExist = true
break
}
}
if (!isExist) {
selected.district = null
}
}
}).catch()
} else {
areaList.district = []
selected.district = null
}
}, { deep: true })
const emits = defineEmits(['complete'])
/**
* 监听区县变更
*/
watch(() => selected.district, (nval)=> {
if (nval) {
currSelect.value = 'district'
emits('complete', selected)
show.value = false
}
}, { deep: true })
const open = ()=> {
show.value = true
}
defineExpose({
open
})
</script>
<style lang="scss" scoped></style>

117
src/components/information-filling/information-filling.vue

@ -1,117 +0,0 @@
<template>
<u-popup :show="show" :round="10" @close="show = false" :closeable="true">
<view class="mx-[30rpx] pb-[20rpx] pt-[40rpx] border-t">
<view class="text-base">{{ t('getAvatarNickname') }}</view>
<view class="text-sm mt-[18rpx] text-gray-400">{{ t('getAvatarNicknameTips') }}</view>
</view>
<u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef">
<view class="mx-[30rpx]">
<view class="mt-[20rpx]">
<u-form-item :label="t('headimg')" prop="headimg" :border-bottom="true">
<button class="m-0 my-[10rpx] p-0 w-[140rpx] h-[140rpx]" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<view class="w-full h-full flex items-center justify-center overflow-hidden">
<u-image :src="img(formData.headimg)" width="140rpx" height="140rpx" v-if="formData.headimg" mode="aspectFill"></u-image>
<u-icon name="plus" v-else></u-icon>
</view>
</button>
</u-form-item>
<u-form-item :label=" t('nickname')" prop="nickname" :border-bottom="true">
<input type="nickname" v-model="formData.nickname" :placeholder="t('nicknamePlaceholder')" @blur="bindNickname">
</u-form-item>
</view>
</view>
<view class="p-[30rpx] mt-[20rpx]">
<u-button type="primary" :loading="loading" :text="t('confirm')" shape="circle" @click="confirm"></u-button>
</view>
</u-form>
</u-popup>
</template>
<script setup lang="ts">
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/locale'
import useMemberStore from '@/stores/member'
import { img } from '@/utils/common'
import { modifyMember } from '@/app/api/member'
import { fetchBase64Image } from '@/app/api/system'
const show = ref(false)
const loading = ref(false)
const memberStore = useMemberStore()
const info = computed(() => memberStore.info)
const formData = reactive({
nickname: '',
headimg: ''
})
watch(() => info.value, () => {
if (info.value) {
formData.nickname = info.value.nickname
formData.headimg = info.value.headimg
}
}, { immediate: true })
const onChooseAvatar = (e) => {
uni.getFileSystemManager().readFile({
filePath: e.detail.avatarUrl, //
encoding: 'base64', //
success: res => {
fetchBase64Image({ content: res.data }).then(uploadRes => {
formData.headimg = uploadRes.data.url
})
}
})
}
const bindNickname = (e) => {
formData.nickname = e.detail.value
}
const rules = {
'headimg': {
type: 'string',
required: true,
message: t('headimgPlaceholder'),
trigger: ['blur', 'change'],
},
'nickname': {
type: 'string',
required: true,
message: t('nicknamePlaceholder'),
trigger: ['blur', 'change'],
}
}
const formRef = ref(null)
const confirm = async () => {
formRef.value.validate().then(async() => {
if (loading.value) return
loading.value = true
//
await modifyMember({ field: 'headimg', value: formData.headimg }).then(() => {
memberStore.info.headimg = formData.headimg
}).catch(() => {
loading.value = false
})
if (!loading.value) return
//
modifyMember({ field: 'nickname', value: formData.nickname }).then(() => {
memberStore.info.nickname = formData.nickname
loading.value = false
show.value = false
}).catch(() => {
loading.value = false
})
})
}
defineExpose({
show
})
</script>
<style lang="scss" scoped></style>

22
src/components/libs/config/index.ts

@ -1,22 +0,0 @@
const version = '3'
// 开发环境才提示,生产环境不会提示
if (process.env.NODE_ENV === 'development') {
console.log(`\n %c lxui-uni V${version} %c https://blog.csdn.net/qq_51091386/article/details/138125947 \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0;', 'color: #3c9cff;background: #ffffff; padding:5px 0;');
}
export let uploadUrl = '/api/v1.Resources/upload'
export let uploadBaseUrl = ''
const setConfig = (config: any) => {
console.log(config)
if (config?.uploadUrl) {
uploadUrl = config.uploadUrl
}
if (config?.uploadBaseUrl) {
uploadBaseUrl = config.uploadBaseUrl
}
}
export default {
version,
setConfig
}

145
src/components/libs/hooks/useListLoadClass/index.ts

@ -1,145 +0,0 @@
/*
* @description:
* @fileName: useListLoadClass.ts
* @author: lxx
* @date: 2023-07-08 08:55:52
* @version: V1.0.0
*/
import { reactive, ref, computed } from "vue"
import { onReachBottom } from "@dcloudio/uni-app"
class LoadDataClass {
// 请求参数
static queryParams = reactive({
page: 1,
limit: 10
})
// 列表数据
list = ref<any[]>([])
total = ref(0)
// 前置处理方法
afterLoadData: Function | undefined
// 请求方法
Query: Function
// 加载状态参数
isLoading = ref(false)
// 无更多数据了
isNoData = computed(() => {
if (LoadDataClass.queryParams.page * LoadDataClass.queryParams.limit >= this.total.value) {
return true
} else {
return false
}
})
// 显示暂无数据
isEmpty = computed(() => {
if (this.total.value === 0) {
return true
} else {
return false
}
})
constructor(apiFunctions: Function, afterLoadData?: Function, options: any = {}) {
this.queryParamsReset()
this.Query = apiFunctions
this.afterLoadData = afterLoadData
// console.log('options', options)
// 存在额外参数拼接
this.setParams(options)
}
// 加载数据
LoadData = async () => {
uni.showLoading({
title: '加载中...'
})
this.isLoading.value = true
const res = await this.Query(LoadDataClass.queryParams)
this.afterLoadData && this.afterLoadData(res)
this.total.value = res.data.total
this.list.value = this.list.value.concat(res.data.data)
uni.hideLoading()
uni.stopPullDownRefresh()
this.isLoading.value = false
}
/**
*
* @param options: 参数
* @param isClear: 是否清空数据 false
*/
setParams = (options: any, isClear: boolean = false) => {
if (isClear) {
this.queryParamsReset()
} else {
LoadDataClass.queryParams.page = 1
}
this.list.value = []
LoadDataClass.queryParams = Object.assign(LoadDataClass.queryParams, options)
// 加载数据
this.LoadData()
}
// 加载更多
LoadMore = () => {
if (this.isNoData.value || this.isLoading.value) return // 无数据或者加载中不进行加载
LoadDataClass.queryParams.page += 1
this.LoadData()
}
// 重置参数
queryParamsReset = () => {
LoadDataClass.queryParams = reactive({
page: 1,
limit: 10
})
}
/**
*
* @param isClear: 是否清空数据
*/
ReLoad = (isClear: boolean = false) => {
this.isLoading.value = false
this.list.value = []
if (isClear) {
this.queryParamsReset()
} else {
LoadDataClass.queryParams.page = 1
}
this.LoadData()
}
}
/**
*
* @param api: ListAPI
* @param afterLoadData: res数据前置处理方法
* @returns
*/
interface LoadDataInt {
api: Function
afterLoadData?: Function
options?: any
isNeedReachBottom?: boolean
}
export function LoadData({ api, afterLoadData, options, isNeedReachBottom = true }: LoadDataInt) {
const data = new LoadDataClass(api, afterLoadData, options)
// 下拉加载
if (isNeedReachBottom) {
onReachBottom(() => {
console.log('onReachBottom')
data.LoadMore()
})
}
return {
list: data.list,
isLoading: data.isLoading,
isNoData: data.isNoData,
isEmpty: data.isEmpty,
ReLoad: data.ReLoad,
setParams: data.setParams,
LoadMore: data.LoadMore
}
}

294
src/components/libs/util/index.ts

@ -1,294 +0,0 @@
/**
*
* @author liux
* @date 2023-08-15 14:17
* @param { string } url
* @param { "navigateTo" | "redirectTo" | "reLaunch" | "switchTab" } [mode=navigateTo]
* @param { object } params
* @example
* goToPage({ url: 'pages/index/index', mode: 'navigateTo', params: {'id': 1} })
* @returns { void }
*/
type pageMode = 'navigateTo' | 'redirectTo' | 'reLaunch' | 'switchTab'
interface goToPageInt {
url: string
mode?: pageMode
params?: {
[n: string]: string | number | boolean
}
}
export const goToPage = ({ url, mode = 'navigateTo', params = {} }: goToPageInt): void => {
if (!url || url.length === 0) {
throw Error('"url" is a required parameter')
}
const urlEncode = (params: any = {}) => {
const result :string[] = []
for (const k in params) {
if (!params[k]) continue
result.push(k + '=' + params[k])
}
return result.join('&')
}
// const storage = JSON.parse(uni.getStorageSync('LX_user'))
// const token = storage?.userInfo?.token
// if(!token) {
// url = 'pages/login/loginXcx'
// mode = 'navigateTo'
// }
const queryStr = !isEmpty(params) ? '?' + urlEncode(params) : ''
const obj = { url: `/${url}${queryStr}` }
// console.log('obj', obj)
switch (mode) {
case 'navigateTo':
uni.navigateTo(obj)
break
case 'redirectTo':
uni.redirectTo(obj)
break
case 'reLaunch':
uni.reLaunch(obj)
break
case 'switchTab':
uni.switchTab(obj)
break
default:
throw Error(`${mode} does not exist`)
}
}
/**
*
* @author liux
* @date 2023-08-15 14:17
* @license MIT
* @param {*} object
* @returns { boolean }
*/
export const isEmptyObject = (object: any): boolean => {
return Object.keys(object).length === 0
}
/**
*
* @author liux
* @date 2023-08-15 14:17
* @license MIT
* @param {*} object
* @returns { boolean }
*/
export const isObject = (object: any): boolean => {
return Object.prototype.toString.call(object) === '[object Object]'
}
/**
*
* @author liux
* @license MIT
* @param {*} object
* @returns { boolean }
*/
export const isArray = (object: any): boolean => {
return Object.prototype.toString.call(object) === '[object Array]'
}
/**
*
* @author liux
* @license MIT
* @param {*} value
* @returns { boolean }
*/
export const isEmpty = (value: any): boolean => {
if (isArray(value)) {
return value.length === 0
}
if (isObject(value)) {
return isEmptyObject(value)
}
return !value
}
/**
*
* @author liux
* @license MIT
* @param { number } time 10 | 13
* @param { string } [format=yyyy-MM-dd] format
* @example
* formatTime(1691744378556, 'yyyy-MM-dd HH:mm:ss')
* @returns { string }
*/
export const formatTime = (time: number, format: string = 'yyyy-MM-dd HH:mm:ss'): string => {
const len = time.toString().trim().length
if (len !== 10 && len !== 13) {
throw Error('"time" is a error parameter')
}
time = len !== 13 ? time * 1000 : time
if (!time) return ''
const date = new Date(time)
const M = (date.getMonth() + 1).toString()
const d = date.getDate().toString()
const H = date.getHours().toString()
const m = date.getMinutes().toString()
const s = date.getSeconds().toString()
const timeObject: {
[n: string]: string
} = {
yyyy: date.getFullYear().toString(),
MM: M.padStart(2, '0'),
dd: d.padStart(2, '0'),
HH: H.padStart(2, '0'),
mm: m.padStart(2, '0'),
ss: s.padStart(2, '0'),
M: M,
d: d,
H: H,
m: m,
s: s
}
const reg = new RegExp(Object.keys(timeObject).join('|'), 'g')
const res = format.replace(reg, (k) => {
return timeObject[k]
})
return res
}
// 判断当前时间是否在一个时间区间内
export const isTimeIn = (start: string, end: string) => {
// 获取当前时间
const currentTime = new Date()
const startArr = start.split(':').map(Number)
// 设置开始时间为10:00
const startTime = new Date()
startTime.setHours(startArr[0], startArr[1])
// 设置结束时间为20:00
const endArr = end.split(':').map(Number)
const endTime = new Date()
endTime.setHours(endArr[0], endArr[1])
// 检查当前时间是否在10:00到20:00之间
if (currentTime >= startTime && currentTime <= endTime) {
// console.log('当前时间在之间')
} else {
// console.log('当前时间不在之间')
}
}
/**
*
* @author liux
* @license MIT
* @param {function} fn
* @param {umber} [wait=1000] wait
* @returns { void }
*/
export const debounce = <T extends (...args: any[]) => any>(fn: T, wait: number = 1000): ((...args: Parameters<T>) => void) => {
let timer: any
return function (this: any, ...args: Parameters<T>) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, wait)
}
}
/**
*
* @author liux
* @date 2023-08-15 14:17
* @license MIT
* @param { function } fn
* @param { number } [wait=1000] wait
*/
export const throttle = <T extends (...args: any[]) => any>(fn: T, wait: number = 1000) => {
let timer: number = Date.now()
return function (this: any, ...args: Parameters<T>) {
if (Date.now() - timer >= wait) {
fn.apply(this, args)
timer = Date.now()
}
}
}
/**
*
* @author liux
* @param { string } url
* @example
* saveImgData('/upload/images/img.png')
* @returns
*/
export const saveImgData = debounce((url: string) => {
uni.showLoading({ title: '图片保存中...', mask: true })
// 判断图片地址是否有http
if (url.indexOf('http') === -1) {
url = import.meta.env.VITE_APP_BASE_URL + url
}
uni.downloadFile({
url,
success: (res: any) => {
if (res.statusCode === 200) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
uni.showToast({
title: '保存成功~',
icon: 'none',
duration: 2000
})
},
fail: () => {
uni.showToast({
title: '保存失败~',
icon: 'none',
duration: 2000
})
},
complete: () => {
uni.hideLoading()
}
})
}
}
})
})
/**
*
* @author liux
* @param { string } data
* @example
* setClipboardData('123456')
* @returns
*/
export const setClipboardData = (data: string) => {
uni.setClipboardData({
data: data,
success: () => {
uni.showToast({
title: '复制成功',
icon: 'none',
duration: 2000
})
}
})
}
export default {
goToPage,
formatTime,
debounce,
throttle,
saveImgData,
setClipboardData
}

133
src/components/liu-checkBox/liu-checkBox.vue

@ -1,133 +0,0 @@
<template>
<view class="zcks-card">
<view class="craditem" v-for="(item,index) in newCheckOptions" :key="index" :class="{'active': item.selected}"
:style="{ '--color': activeColor }" @click="selectData(index,item)">
{{item.sku_name}}
</view>
</view>
</template>
<script>
export default {
props: {
//
checkOptions: {
type: Array,
default () {
return []
}
},
//(single)
checkType: {
type: String,
default: ''
},
//()
maxNum: {
type: Number,
},
//
activeColor: {
type: String,
default: '#0A4B9D'
}
},
watch: {
checkOptions: {
deep: true,
immediate: true,
handler(parentArry) {
this.newCheckOptions = parentArry
},
}
},
data() {
return {
newCheckOptions: []
}
},
methods: {
selectData(index, item) {
if (this.checkType == 'single') { //
this.newCheckOptions.forEach(mess => {
mess.selected = false
})
this.$set(item, 'selected', true)
} else {
let chooseData = this.newCheckOptions.filter(mes => mes.selected)
if (this.maxNum && !item.selected && chooseData.length >= this.maxNum) {
uni.showToast({
title: '最多可选' + this.maxNum + '项',
icon: 'none'
})
return
}
this.$set(item, 'selected', item.selected ? false : true)
}
this.newCheckOptions = JSON.parse(JSON.stringify(this.newCheckOptions))
let chooseOnData = this.newCheckOptions.filter(mes => mes.selected)
this.$emit("checkChange", chooseOnData)
},
//
reset() {
this.newCheckOptions.forEach(mess => {
mess.selected = false
})
}
}
}
</script>
<style scoped>
.zcks-card {
display: flex;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
}
.craditem {
padding: 8rpx 24rpx;
margin-right: 24rpx;
margin-bottom: 16rpx;
background: #FFFFFF;
border-radius: 4rpx;
border: 2rpx solid #999999;
font-size: 30rpx;
font-weight: 400;
color: #333333;
line-height: 48rpx;
text-align: left;
}
.active {
border: 2rpx solid var(--color);
color: var(--color);
position: relative;
}
.active::after {
content: "";
position: absolute;
top: 0;
right: 0;
width: 0;
height: 0;
border-top: 42rpx solid var(--color);
border-left: 42rpx solid transparent;
}
.active::before {
width: 8rpx;
height: 12rpx;
border-color: #FFFFFF;
border-style: solid;
border-width: 0 1rpx 2rpx 0;
content: "";
position: absolute;
right: 8rpx;
top: 2rpx;
z-index: 3;
transform: rotate(45deg);
}
</style>

249
src/components/lx-header/lx-header.vue

@ -1,249 +0,0 @@
<script lang="ts" setup>
/*
* @description: 头部组件
* @fileName: Header.vue
* @params
* @author: lxx
* @date: 2023-07-16 09:32:09
* @version: V1.0.2
*/
import { goToPage } from '../libs/util'
import { type CSSProperties, computed, ref, watchEffect } from 'vue'
type headerInt = {
title: string
// 44px
headerHeight?: number
//
leftIconShow?: boolean
//
backgroundColor?: string
backgroundColor2?: string
textColor?: string
textFontSize?: number
//
isShowHeaderBox?: boolean
positionState?: string
isShowShadow?: boolean
isBlackIcon?: boolean
// true false()
isHaveRightBox?: boolean
}
const props = withDefaults(defineProps<headerInt>(), {
// 44px
headerHeight: 44,
//
leftIconShow: true,
//
backgroundColor: '#ffffff', //linear-gradient(90deg, rgba(10, 207, 254, 1) 0%, rgba(74, 92, 255, 1) 100%)
backgroundColor2: '#ffffff',
textColor: '#000',
textFontSize: 34,
title: '标题',
//
isShowHeaderBox: true,
positionState: 'fixed',
isShowShadow: false,
isBlackIcon: true, //
isHaveRightBox: true
})
let { statusBarHeight } = uni.getSystemInfoSync()
// #ifdef MP-WEIXIN
//
let menuButton = uni.getMenuButtonBoundingClientRect()
//
let wxHeaderWidth = menuButton.left - 10
//
statusBarHeight = menuButton.top
// #endif
// padding
const fillBoxHeight = ref(statusBarHeight + 3)
// header
const headerHeightRef = ref(0)
watchEffect(() => {
headerHeightRef.value = props.headerHeight
// #ifdef MP-WEIXIN
//
headerHeightRef.value = menuButton.height
// #endif
})
// console.log('statusBarHeight', statusBarHeight)
const style = computed(() => {
return {
boxShadow: props.isShowShadow ? '0 0 8rpx -3rpx #333' : '0 0 0 0 #333',
background: props.backgroundColor,
color: props.textColor,
position: props.positionState
} as CSSProperties
})
// ()
const goBack = () => {
if (getCurrentPages().length <= 1) {
goToPage({
url: 'pages/index/index',
mode: 'redirectTo'
})
} else {
uni.navigateBack()
}
}
const backIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAACMklEQVR4nO2av0uVURjHPyiEYgkuoVC4ODSK2pRDYFOENOQP6A+IEgknkWgKgqTEP6AaHIKyhqhGiSgVpKwGBweLBMHZcjAK47xcXrxxn6el5/pwzvnAme5wv5/ve+/hPO/7kslkMplMxjsjwCdgB7iV2tW6Aez/tW46yFUXpmvIh/U6AXfuCvJhnXOQz5R7ivz1iL0LZhX5Sw7ymTIviG8DpyP2pg14Kch/BjodZDSjHdgQ5D8CrZF6F3QD3wT5VeCog4xm9ADfBfn3scv3KvJhL2hxkNGMsJv/EOQfRepcchbYFeQnnWQ046JywLkSqXPJlCJ/2UlGM7Rz/WikziX3BfFfwLCTjGY8FuR/Av2ROpdIQ004159yktGMZ4L8Yuzn+mZgQZB/BTQ6yGhGOLevCPIvInUu6QK+CPLPnWQ05Z0gPxuxcxXSIWfAUUZT5oQCfgPXIvau4onyS5hwlNOUB0oJcxF7V6ENPuFofMRRVjNuKyW8SaWEIaWEt0CTg4zmDCq3v5ZSKUG7AboMdDjIaE6fUsJX4ETk/gU9lddbapWwncLzfiqPwTaVzfGCg4zmdCiTYxJ3iPnHo/CwrjrIWBeeKiWMJeBfoM0P4w7y1YUZpYQ7CfgXaEPUtIN8dUF6KXK/cqu9IYEOincApRIWU5kfxpUS1oDjDjKacx7YE0rYSGWI6lXmh4f/60s8bywfKkPUVo3Pjh1CnkPjJLB+4OqHv8aZlArIZDKZTMYC4A8EQ0uQY7/3uAAAAABJRU5ErkJggg=='
const backIconW = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAACjElEQVR4nO2azatNURiH364S+SgTuUUmBoa6GDEwuCNJBr7KHyAkGUkyUsoN3T8AA0r5GugyUZK4lHwODAwQpYwvBqQeve4+deK8a5+zz9q3d6+9nnonp7PXWb/n7L1aH1symUwmk8lUBpiL2gO8BmaAU/p7bpiD8Cf4n5NtETDRI7zywEH0WWoMf9YIr4x7yP6XmsKfC4Q/kvoYMBkIv7PzPTdEDn/DCP4V2Nj93dQELAPuGOHfAKv/vcYNEcKvAN4b4V8BS3tdl4qAdcAnI/xLYLF1rRuGCD8GfDPCPw+FT0HA+kB4HQsWlbXhhgrhdTT/boS/2m87TRWwBfhhhD82SFtuGKDTO4zgyv5B76SmCTgeCL+vyjjihj46G5rX7604iDZGwAUj+G9gd9XwTRFwzQj/C9g8TPgmCLAWNTqvXztseO8Cbhnhp615fSoCFgL3jfB3gXmxwnsUoPP2Z0b4qZjBPQpYA3wwwt+uI7w3AY+N8JN1hY8pYCRCG5uMz6citF07MQRcMT6/JyIHHWWth+KWvG48BsrR1MeATl0MSLjcBgFSsvDRqfH81AVonQ5IeBhLghuMDu4KSHgELEhdgNb2wPbXk2EluKGko6EN0KfAaOoCtDYEJHwEVqYuQIpDkBlDgh58jqcuQIpjsM+BwXFb6gKkeOatlSOD7BC7ocLzGzoKVw6kLqBTNwMSDrVBgJSsHw63QYDW+YCEM20QICWLqIk2CJDAS5EUW+0jqQuQ4h1Ai+nu9YMbIguQYvCzeAssT12A1lbgpyFB3ygbdRB9lpoESPEekbV+uBSr/zF2hevihYiMiciXHu0vcdPLGu+ATq0C3nX9+/poWGcRmUwmk8lk+kRE/gCfWLdyj0KPNgAAAABJRU5ErkJggg=='
</script>
<template>
<!-- #ifndef MP-TOUTIAO -->
<view class="header_box">
<view class="header_main" :style="style">
<view class="status_bar" :style="{ height: statusBarHeight + 'px' }"></view>
<!-- 标准的左中右结构 -->
<view v-if="isHaveRightBox" class="header flex-center-between" :style="{ height: headerHeightRef + 'px' }">
<view class="header_left flex-center-between">
<slot name="left">
<view v-if="leftIconShow" class="icon flex" @click="goBack">
<image :src="isBlackIcon ? backIcon : backIconW" mode="widthFix"></image>
</view>
</slot>
</view>
<view class="header_center">
<slot>
<view class="title" :style="{ fontSize: textFontSize + 'rpx' }">
{{ title }}
</view>
</slot>
</view>
<view class="header_right flex">
<!-- #ifndef MP-WEIXIN -->
<slot name="right"></slot>
<!-- #endif -->
</view>
</view>
<!-- 左右结构 -->
<view v-else class="wx_header flex"
:style="{ height: headerHeightRef + 'px', width: wxHeaderWidth + 'px' }">
<view class="wx_header_left flex">
<slot name="left">
<view class="icon flex" @click="goBack" v-if="leftIconShow">
<image :src="isBlackIcon ? backIcon : backIconW" mode="widthFix"></image>
</view>
</slot>
</view>
<view class="wx_header_txt flex">
<slot name="center">
<view class="title" :style="{ fontSize: textFontSize + 'rpx' }">
{{ title }}
</view>
</slot>
</view>
</view>
</view>
<!-- 填充头部防止塌陷 新加v-if="isShowHeaderBox" -->
<view class="status_bar" v-if="isShowHeaderBox" :style="{ height: fillBoxHeight + 'px' }"></view>
<!-- #ifdef MP-WEIXIN -->
<view v-if="isShowHeaderBox" :style="{ height: headerHeightRef + 4 + 'px', background: backgroundColor2 }">
</view>
<!-- #endif -->
<!-- 待测试 -->
<!-- #ifndef MP-WEIXIN -->
<view v-if="isShowHeaderBox" :style="{ height: headerHeightRef + 'px', background: backgroundColor2 }"></view>
<!-- #endif -->
</view>
<!-- #endif -->
</template>
<style lang="scss" scoped>
.header_box {
// padding: 0 20rpx 5rpx;
// background-color: #fff;
}
.header_main {
width: 100%;
z-index: 9999;
top: 0;
left: 0;
/* #ifdef MP-WEIXIN */
padding: 0 20rpx 15rpx;
/* #endif */
/* #ifndef MP-WEIXIN */
padding: 5rpx 20rpx;
/* #endif */
box-sizing: border-box;
.img {
width: 48rpx;
height: 48rpx;
}
.header {
// padding: 0 16rpx;
box-sizing: border-box;
.header_left {
width: 20%;
.icon {
width: 48rpx;
}
.left_txt {
font-size: 22rpx;
line-height: 22rpx;
}
}
.header_center {
width: 60%;
text-align: center;
font-size: 28rpx;
.title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.header_right {
width: 20%;
}
}
.wx_header {
.wx_header_left {
height: 100%;
.icon {
width: 48rpx;
image {
vertical-align: middle;
width: 100%;
}
}
}
.wx_header_txt {
flex: 1;
height: 100%;
padding-left: 26rpx;
.title {
width: 100%;
line-height: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 28rpx;
}
}
}
}
</style>
../../libs/util

35
src/components/lx-img/lx-img.vue

@ -1,35 +0,0 @@
<template>
<image :src="imgUrl" :style="{ 'width': width, 'height': height, 'border-radius': round }" :mode="mode" />
</template>
<script lang="ts" setup>
import { computed } from 'vue';
interface propInt {
imgUrl: string
mode: string
width: string
height: string
round: string
}
const props = withDefaults(defineProps<propInt>(), {
//
src: '',
//
mode: '',
//
width: '100%',
//
height: '100%',
//
round: ''
})
const imgUrl = computed(() => {
return import.meta.env.VITE_IMG_DOMAIN + props.src
})
</script>
<style lang="scss" scoped></style>

92
src/components/lx-list-state/lx-list-state.vue

File diff suppressed because one or more lines are too long

102
src/components/lx-list/lx-list.vue

@ -1,102 +0,0 @@
<script setup lang="ts">
/*
* @description: 分页组件
* @fileName: List.vue
* @params {Function} api : 数据请求的接口API
* @params {Function} afterLoadData : 数据请求完毕前置处理方法
* @params {object: any} options : 数据请求的额外参数
* @params {'default' | 'scrollView'} listType : scrollView为scroll-view包裹的分页
* @ref setListParams {Function} : 使用==>ref.value.setListParams(obj, isClear) obj:请求额外的参数 isClear是否清空请求参数
* @author: lxx
* @date: 2023-07-28 11:36:23
* @update: 2023-10-13 10:34:03
* @version: V1.0.1
*/
import { LoadData } from '../libs/hooks/useListLoadClass'
import { toRefs, ref } from 'vue';
import lxListState from '../lx-list-state/lx-list-state.vue'
interface listPropsInt {
api: Function
afterLoadData?: Function
options?: any
listType?: 'default' | 'scrollView'
}
//
// 3.2.x defineProps
const props = withDefaults(defineProps<listPropsInt>(), {
api: () => ({}),
// eslint-disable-next-line @typescript-eslint/no-empty-function
options: {},
listType: 'default'
})
let { options, api, afterLoadData } = toRefs(props)
// console.log('props', props.options, options.value)
//
let { list, isLoading, isEmpty, isNoData, setParams, LoadMore } = LoadData({
api: api.value,
afterLoadData: afterLoadData?.value,
options: options.value
})
//
// const inputTxt = ref('')
// const inputChange = debounce(() => {
// console.log('input change')
// setParams({ search: inputTxt.value })
// })
// 使--> isClear true
const setListParams = (obj: any, isClear = true) => {
const param = ref<{
search?: string
[key: string]: any
}>({})
// if (props.isNeedSearch) {
// param.value.search = inputTxt.value
// }
param.value = { ...obj }
setParams({ ...param.value }, isClear)
}
defineExpose({
list,
setListParams,
LoadMore
})
//
const scrollTop = ref(0)
const scroll = (e: any) => {
scrollTop.value = e.detail.scrollTop
}
const emit = defineEmits(['scrollTolower'])
const scrolltolower = () => {
console.log('触底啦!')
LoadMore()
emit('scrollTolower')
}
</script>
<template>
<template v-if="listType == 'default'">
<view class="lxx_list_box">
<view class="lxx_list_box_content">
<template v-for="(item, index) in list" :key="index">
<slot v-bind:item="item" v-bind:index="index"></slot>
</template>
</view>
<lxListState :is-empty="isEmpty" :is-loading="isLoading" :is-no-data="isNoData"></lxListState>
</view>
</template>
<template v-else>
<scroll-view scroll-y="true" class="scroll-Y" @scroll="scroll" @scrolltolower="scrolltolower">
<view class="lxx_list_box">
<view v-for="(item, index) in list" :key="index">
<slot v-bind:item="item" v-bind:index="index"></slot>
</view>
</view>
<lxListState :is-empty="isEmpty" :is-loading="isLoading" :is-no-data="isNoData"></lxListState>
</scroll-view>
</template>
</template>
<style lang="scss" scoped></style>

66
src/components/mescroll/hooks/useMescroll.js

@ -1,66 +0,0 @@
// 小程序无法在hook中使用页面级别生命周期,需单独传入: https://ask.dcloud.net.cn/question/161173
// import { onPageScroll, onReachBottom, onPullDownRefresh} from '@dcloudio/uni-app';
/**
* 初始化mescroll, 相当于vue2的mescroll-mixins.js文件 (mescroll-body mescroll-uni 通用)
* mescroll-body需传入onPageScroll, onReachBottom
* mescroll-uni无需传onPageScroll, onReachBottom
* 当down.native为true时,需传入onPullDownRefresh
*/
function useMescroll(onPageScroll, onReachBottom, onPullDownRefresh){
// mescroll实例对象
let mescroll = null;
// mescroll组件初始化的回调,可获取到mescroll对象
const mescrollInit = (e)=> {
mescroll = e;
}
// 获取mescroll对象, mescrollInit执行之后会有值, 生命周期created中会有值
const getMescroll = ()=>{
return mescroll
}
// 下拉刷新的回调 (mixin默认resetUpScroll)
const downCallback = ()=> {
if(mescroll.optUp.use){
mescroll.resetUpScroll()
}else{
setTimeout(()=>{
mescroll.endSuccess();
}, 500)
}
}
// 上拉加载的回调
const upCallback = ()=> {
// mixin默认延时500自动结束加载
setTimeout(()=>{
mescroll.endErr();
}, 500)
}
// 注册系统自带的下拉刷新 (配置down.native为true时生效, 还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
onPullDownRefresh && onPullDownRefresh(() => {
mescroll && mescroll.onPullDownRefresh();
})
// 注册列表滚动事件,用于判定在顶部可下拉刷新,在指定位置可显示隐藏回到顶部按钮 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
onPageScroll && onPageScroll(e=>{
mescroll && mescroll.onPageScroll(e);
})
// 注册滚动到底部的事件,用于上拉加载 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
onReachBottom && onReachBottom(()=>{
mescroll && mescroll.onReachBottom();
})
return {
getMescroll,
mescrollInit,
downCallback,
upCallback
}
}
export default useMescroll

56
src/components/mescroll/hooks/useMescrollComp.js

@ -1,56 +0,0 @@
import { ref } from 'vue';
// 小程序无法在hook中使用页面级别生命周期,需单独传入: https://ask.dcloud.net.cn/question/161173
// import { onPageScroll, onReachBottom, onPullDownRefresh} from '@dcloudio/uni-app';
/**
* mescroll-body写在子组件时,需通过useMescrollComp补充子组件缺少的生命周期, 相当于vue2的mescroll-comp.js文件
* 必须传入onPageScroll, onReachBottom
* 当down.native为true时,需传入onPullDownRefresh
*/
function useMescrollComp(onPageScroll, onReachBottom, onPullDownRefresh){
// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
onPageScroll(e=>{
handlePageScroll(e)
})
onReachBottom(()=>{
handleReachBottom()
})
// 当down的native: true时, 还需传递此方法进到子组件
onPullDownRefresh && onPullDownRefresh(()=>{
handlePullDownRefresh()
})
const mescrollItem = ref(null)
const handlePageScroll = (e)=>{
const mescroll = getMescroll()
mescroll && mescroll.onPageScroll(e);
}
const handleReachBottom = ()=>{
const mescroll = getMescroll()
mescroll && mescroll.onReachBottom();
}
const handlePullDownRefresh = ()=>{
const mescroll = getMescroll()
mescroll && mescroll.onPullDownRefresh();
}
const getMescroll = ()=>{
if(mescrollItem.value && mescrollItem.value.getMescroll){
return mescrollItem.value.getMescroll()
}
return null
}
return {
mescrollItem,
getMescroll
}
}
export default useMescrollComp

69
src/components/mescroll/hooks/useMescrollMore.js

@ -1,69 +0,0 @@
import { ref } from 'vue';
// 小程序无法在hook中使用页面级别生命周期,需单独传入: https://ask.dcloud.net.cn/question/161173
// import { onPageScroll, onReachBottom, onPullDownRefresh} from '@dcloudio/uni-app';
/** mescroll-more示例写在子组件时,需通过useMescrollMore补充子组件缺少的生命周期, 相当于vue2的mescroll-more.js文件 */
function useMescrollMore(mescrollItems, onPageScroll, onReachBottom, onPullDownRefresh){
// 当前tab下标
const tabIndex = ref(0)
// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
onPageScroll && onPageScroll(e=>{
handlePageScroll(e)
})
onReachBottom && onReachBottom(()=>{
handleReachBottom()
})
// 当down的native: true时, 还需传递此方法进到子组件
onPullDownRefresh && onPullDownRefresh(()=>{
handlePullDownRefresh()
})
const handlePageScroll = (e)=>{
let mescroll = getMescroll(tabIndex.value);
mescroll && mescroll.onPageScroll(e);
}
const handleReachBottom = ()=>{
let mescroll = getMescroll(tabIndex.value);
mescroll && mescroll.onReachBottom();
}
const handlePullDownRefresh = ()=>{
let mescroll = getMescroll(tabIndex.value);
mescroll && mescroll.onPullDownRefresh();
}
// 根据下标获取对应子组件的mescroll
const getMescroll = (i)=>{
if (mescrollItems && mescrollItems[i]) {
return mescrollItems[i].value.getMescroll()
} else{
return null
}
}
// 切换tab,恢复滚动条位置
const scrollToLastY = ()=>{
let mescroll = getMescroll(tabIndex.value);
if(mescroll){
// 恢复上次滚动条的位置
let y = mescroll.getScrollTop()
mescroll.scrollTo(y, 0)
// 再次恢复上次滚动条的位置, 确保元素已渲染
setTimeout(()=>{
mescroll.scrollTo(y, 0)
},20)
}
}
return {
tabIndex,
getMescroll,
scrollToLastY
}
}
export default useMescrollMore

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save