项目初始化
parent
cde51de204
commit
4ed701fa0f
|
|
@ -0,0 +1,14 @@
|
|||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = false
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
VUE_APP_LANGUAGE = 'uniapp'
|
||||
VUE_APP_VERSION = '1.3.6'
|
||||
VUE_APP_VERSION_CODE = 136
|
||||
VUE_APP_SERVICE='https://tb.53kf.com/code/app/bba10e62b32391cdefb75e35d59943237/1'
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
ENV = 'development'
|
||||
|
||||
VUE_APP_BASE_API = 'https://jiqu.zichunsheng.cn/api'
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
ENV = 'production'
|
||||
|
||||
VUE_APP_BASE_API = 'https://jiqu.zichunsheng.cn/api'
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
build/*.js
|
||||
src/assets
|
||||
public
|
||||
dist
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint',
|
||||
sourceType: 'module'
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
extends: ['plugin:vue/recommended', 'eslint:recommended'],
|
||||
|
||||
// add your custom rules here
|
||||
//it is base on https://github.com/vuejs/eslint-config-vue
|
||||
rules: {
|
||||
"vue/max-attributes-per-line": [2, {
|
||||
"singleline": 10,
|
||||
"multiline": {
|
||||
"max": 1,
|
||||
"allowFirstLine": false
|
||||
}
|
||||
}],
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"vue/multiline-html-element-content-newline": "off",
|
||||
"vue/name-property-casing": ["error", "PascalCase"],
|
||||
"vue/no-v-html": "off",
|
||||
'accessor-pairs': 2,
|
||||
'arrow-spacing': [2, {
|
||||
'before': true,
|
||||
'after': true
|
||||
}],
|
||||
'block-spacing': [2, 'always'],
|
||||
'brace-style': [2, '1tbs', {
|
||||
'allowSingleLine': true
|
||||
}],
|
||||
'camelcase': [0, {
|
||||
'properties': 'always'
|
||||
}],
|
||||
'comma-dangle': [2, 'never'],
|
||||
'comma-spacing': [2, {
|
||||
'before': false,
|
||||
'after': true
|
||||
}],
|
||||
'comma-style': [2, 'last'],
|
||||
'constructor-super': 2,
|
||||
'curly': [2, 'multi-line'],
|
||||
'dot-location': [2, 'property'],
|
||||
'eol-last': 2,
|
||||
'eqeqeq': ["error", "always", { "null": "ignore" }],
|
||||
'generator-star-spacing': [2, {
|
||||
'before': true,
|
||||
'after': true
|
||||
}],
|
||||
'handle-callback-err': [2, '^(err|error)$'],
|
||||
'indent': [2, 2, {
|
||||
'SwitchCase': 1
|
||||
}],
|
||||
'jsx-quotes': [2, 'prefer-single'],
|
||||
'key-spacing': [2, {
|
||||
'beforeColon': false,
|
||||
'afterColon': true
|
||||
}],
|
||||
'keyword-spacing': [2, {
|
||||
'before': true,
|
||||
'after': true
|
||||
}],
|
||||
'new-cap': [2, {
|
||||
'newIsCap': true,
|
||||
'capIsNew': false
|
||||
}],
|
||||
'new-parens': 2,
|
||||
'no-array-constructor': 2,
|
||||
'no-caller': 2,
|
||||
'no-console': 'off',
|
||||
'no-class-assign': 2,
|
||||
'no-cond-assign': 2,
|
||||
'no-const-assign': 2,
|
||||
'no-control-regex': 0,
|
||||
'no-delete-var': 2,
|
||||
'no-dupe-args': 2,
|
||||
'no-dupe-class-members': 2,
|
||||
'no-dupe-keys': 2,
|
||||
'no-duplicate-case': 2,
|
||||
'no-empty-character-class': 2,
|
||||
'no-empty-pattern': 2,
|
||||
'no-eval': 2,
|
||||
'no-ex-assign': 2,
|
||||
'no-extend-native': 2,
|
||||
'no-extra-bind': 2,
|
||||
'no-extra-boolean-cast': 2,
|
||||
'no-extra-parens': [2, 'functions'],
|
||||
'no-fallthrough': 2,
|
||||
'no-floating-decimal': 2,
|
||||
'no-func-assign': 2,
|
||||
'no-implied-eval': 2,
|
||||
'no-inner-declarations': [2, 'functions'],
|
||||
'no-invalid-regexp': 2,
|
||||
'no-irregular-whitespace': 2,
|
||||
'no-iterator': 2,
|
||||
'no-label-var': 2,
|
||||
'no-labels': [2, {
|
||||
'allowLoop': false,
|
||||
'allowSwitch': false
|
||||
}],
|
||||
'no-lone-blocks': 2,
|
||||
'no-mixed-spaces-and-tabs': 2,
|
||||
'no-multi-spaces': 2,
|
||||
'no-multi-str': 2,
|
||||
'no-multiple-empty-lines': [2, {
|
||||
'max': 1
|
||||
}],
|
||||
'no-native-reassign': 2,
|
||||
'no-negated-in-lhs': 2,
|
||||
'no-new-object': 2,
|
||||
'no-new-require': 2,
|
||||
'no-new-symbol': 2,
|
||||
'no-new-wrappers': 2,
|
||||
'no-obj-calls': 2,
|
||||
'no-octal': 2,
|
||||
'no-octal-escape': 2,
|
||||
'no-path-concat': 2,
|
||||
'no-proto': 2,
|
||||
'no-redeclare': 2,
|
||||
'no-regex-spaces': 2,
|
||||
'no-return-assign': [2, 'except-parens'],
|
||||
'no-self-assign': 2,
|
||||
'no-self-compare': 2,
|
||||
'no-sequences': 2,
|
||||
'no-shadow-restricted-names': 2,
|
||||
'no-spaced-func': 2,
|
||||
'no-sparse-arrays': 2,
|
||||
'no-this-before-super': 2,
|
||||
'no-throw-literal': 2,
|
||||
'no-trailing-spaces': 2,
|
||||
'no-undef': 2,
|
||||
'no-undef-init': 2,
|
||||
'no-unexpected-multiline': 2,
|
||||
'no-unmodified-loop-condition': 2,
|
||||
'no-unneeded-ternary': [2, {
|
||||
'defaultAssignment': false
|
||||
}],
|
||||
'no-unreachable': 2,
|
||||
'no-unsafe-finally': 2,
|
||||
'no-unused-vars': [2, {
|
||||
'vars': 'all',
|
||||
'args': 'none'
|
||||
}],
|
||||
'no-useless-call': 2,
|
||||
'no-useless-computed-key': 2,
|
||||
'no-useless-constructor': 2,
|
||||
'no-useless-escape': 0,
|
||||
'no-whitespace-before-property': 2,
|
||||
'no-with': 2,
|
||||
'one-var': [2, {
|
||||
'initialized': 'never'
|
||||
}],
|
||||
'operator-linebreak': [2, 'after', {
|
||||
'overrides': {
|
||||
'?': 'before',
|
||||
':': 'before'
|
||||
}
|
||||
}],
|
||||
'padded-blocks': [2, 'never'],
|
||||
'quotes': [2, 'single', {
|
||||
'avoidEscape': true,
|
||||
'allowTemplateLiterals': true
|
||||
}],
|
||||
'semi': [2, 'never'],
|
||||
'semi-spacing': [2, {
|
||||
'before': false,
|
||||
'after': true
|
||||
}],
|
||||
'space-before-blocks': [2, 'always'],
|
||||
'space-before-function-paren': [2, 'never'],
|
||||
'space-in-parens': [2, 'never'],
|
||||
'space-infix-ops': 2,
|
||||
'space-unary-ops': [2, {
|
||||
'words': true,
|
||||
'nonwords': false
|
||||
}],
|
||||
'spaced-comment': [2, 'always', {
|
||||
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
|
||||
}],
|
||||
'template-curly-spacing': [2, 'never'],
|
||||
'use-isnan': 2,
|
||||
'valid-typeof': 2,
|
||||
'wrap-iife': [2, 'any'],
|
||||
'yield-star-spacing': [2, 'both'],
|
||||
'yoda': [2, 'never'],
|
||||
'prefer-const': 2,
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||
'object-curly-spacing': [2, 'always', {
|
||||
objectsInObjects: false
|
||||
}],
|
||||
'array-bracket-spacing': [2, 'never']
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
.hbuilderx/
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.project
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw*
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 150
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# mall-app
|
||||
|
||||
## Project setup
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
yarn serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
yarn build
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
const plugins = []
|
||||
|
||||
if (process.env.UNI_OPT_TREESHAKINGNG) {
|
||||
plugins.push(require('@dcloudio/vue-cli-plugin-uni-optimize/packages/babel-plugin-uni-api/index.js'))
|
||||
}
|
||||
|
||||
if (
|
||||
(
|
||||
process.env.UNI_PLATFORM === 'app-plus' &&
|
||||
process.env.UNI_USING_V8
|
||||
) ||
|
||||
(
|
||||
process.env.UNI_PLATFORM === 'h5' &&
|
||||
process.env.UNI_H5_BROWSER === 'builtin'
|
||||
)
|
||||
) {
|
||||
const path = require('path')
|
||||
|
||||
const isWin = /^win/.test(process.platform)
|
||||
|
||||
const normalizePath = path => (isWin ? path.replace(/\\/g, '/') : path)
|
||||
|
||||
const input = normalizePath(process.env.UNI_INPUT_DIR)
|
||||
try {
|
||||
plugins.push([
|
||||
require('@dcloudio/vue-cli-plugin-hbuilderx/packages/babel-plugin-console'),
|
||||
{
|
||||
file (file) {
|
||||
file = normalizePath(file)
|
||||
if (file.indexOf(input) === 0) {
|
||||
return path.relative(input, file)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
])
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
process.UNI_LIBRARIES = process.UNI_LIBRARIES || ['@dcloudio/uni-ui']
|
||||
process.UNI_LIBRARIES.forEach(libraryName => {
|
||||
plugins.push([
|
||||
'import',
|
||||
{
|
||||
'libraryName': libraryName,
|
||||
'customName': (name) => {
|
||||
return `${libraryName}/lib/${name}/${name}`
|
||||
}
|
||||
}
|
||||
])
|
||||
})
|
||||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@vue/app',
|
||||
{
|
||||
modules: 'commonjs',
|
||||
useBuiltIns: process.env.UNI_PLATFORM === 'h5' ? 'usage' : 'entry'
|
||||
}
|
||||
]
|
||||
],
|
||||
plugins
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,109 @@
|
|||
{
|
||||
"name": "mall-app",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "npm run dev:h5",
|
||||
"build": "npm run build:h5",
|
||||
"build:app-plus": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build",
|
||||
"build:custom": "cross-env NODE_ENV=production uniapp-cli custom",
|
||||
"build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build",
|
||||
"build:mp-360": "cross-env NODE_ENV=production UNI_PLATFORM=mp-360 vue-cli-service uni-build",
|
||||
"build:mp-alipay": "cross-env NODE_ENV=production UNI_PLATFORM=mp-alipay vue-cli-service uni-build",
|
||||
"build:mp-baidu": "cross-env NODE_ENV=production UNI_PLATFORM=mp-baidu vue-cli-service uni-build",
|
||||
"build:mp-kuaishou": "cross-env NODE_ENV=production UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build",
|
||||
"build:mp-lark": "cross-env NODE_ENV=production UNI_PLATFORM=mp-lark vue-cli-service uni-build",
|
||||
"build:mp-qq": "cross-env NODE_ENV=production UNI_PLATFORM=mp-qq vue-cli-service uni-build",
|
||||
"build:mp-toutiao": "cross-env NODE_ENV=production UNI_PLATFORM=mp-toutiao vue-cli-service uni-build",
|
||||
"build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build",
|
||||
"build:quickapp-native": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-native vue-cli-service uni-build",
|
||||
"build:quickapp-webview": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview vue-cli-service uni-build",
|
||||
"build:quickapp-webview-huawei": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build",
|
||||
"build:quickapp-webview-union": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build",
|
||||
"dev:app-plus": "cross-env NODE_ENV=development UNI_PLATFORM=app-plus vue-cli-service uni-build --watch",
|
||||
"dev:custom": "cross-env NODE_ENV=development uniapp-cli custom",
|
||||
"dev:h5": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve",
|
||||
"dev:mp-360": "cross-env NODE_ENV=development UNI_PLATFORM=mp-360 vue-cli-service uni-build --watch",
|
||||
"dev:mp-alipay": "cross-env NODE_ENV=development UNI_PLATFORM=mp-alipay vue-cli-service uni-build --watch",
|
||||
"dev:mp-baidu": "cross-env NODE_ENV=development UNI_PLATFORM=mp-baidu vue-cli-service uni-build --watch",
|
||||
"dev:mp-kuaishou": "cross-env NODE_ENV=development UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build --watch",
|
||||
"dev:mp-lark": "cross-env NODE_ENV=development UNI_PLATFORM=mp-lark vue-cli-service uni-build --watch",
|
||||
"dev:mp-qq": "cross-env NODE_ENV=development UNI_PLATFORM=mp-qq vue-cli-service uni-build --watch",
|
||||
"dev:mp-toutiao": "cross-env NODE_ENV=development UNI_PLATFORM=mp-toutiao vue-cli-service uni-build --watch",
|
||||
"dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch --minimize",
|
||||
"dev:quickapp-native": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-native vue-cli-service uni-build --watch",
|
||||
"dev:quickapp-webview": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview vue-cli-service uni-build --watch",
|
||||
"dev:quickapp-webview-huawei": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build --watch",
|
||||
"dev:quickapp-webview-union": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build --watch",
|
||||
"info": "node node_modules/@dcloudio/vue-cli-plugin-uni/commands/info.js",
|
||||
"serve:quickapp-native": "node node_modules/@dcloudio/uni-quickapp-native/bin/serve.js",
|
||||
"test:android": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=android jest -i",
|
||||
"test:h5": "cross-env UNI_PLATFORM=h5 jest -i",
|
||||
"test:ios": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=ios jest -i",
|
||||
"test:mp-baidu": "cross-env UNI_PLATFORM=mp-baidu jest -i",
|
||||
"test:mp-weixin": "cross-env UNI_PLATFORM=mp-weixin jest -i"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app-plus": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-h5": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-helper-json": "*",
|
||||
"@dcloudio/uni-i18n": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-mp-360": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-mp-alipay": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-mp-baidu": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-mp-kuaishou": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-mp-lark": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-mp-qq": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-mp-toutiao": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-mp-vue": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-mp-weixin": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-quickapp-native": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-quickapp-webview": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-stat": "^2.0.0-32920211122002",
|
||||
"@vue/shared": "^3.0.0",
|
||||
"core-js": "^3.6.5",
|
||||
"flyio": "^0.6.2",
|
||||
"jweixin-module": "^1.6.0",
|
||||
"luch-request": "^3.0.7",
|
||||
"regenerator-runtime": "^0.12.1",
|
||||
"uuid": "^8.3.2",
|
||||
"uview-ui": "^1.8.4",
|
||||
"vconsole": "^3.10.0",
|
||||
"vue": "^2.6.11",
|
||||
"vuex": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/runtime": "~7.12.0",
|
||||
"@dcloudio/types": "*",
|
||||
"@dcloudio/uni-automator": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-cli-i18n": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-cli-shared": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-migration": "^2.0.0-32920211122002",
|
||||
"@dcloudio/uni-template-compiler": "^2.0.0-32920211122002",
|
||||
"@dcloudio/vue-cli-plugin-hbuilderx": "^2.0.0-32920211122002",
|
||||
"@dcloudio/vue-cli-plugin-uni": "^2.0.0-32920211122002",
|
||||
"@dcloudio/vue-cli-plugin-uni-optimize": "^2.0.0-32920211122002",
|
||||
"@dcloudio/webpack-uni-mp-loader": "^2.0.0-32920211122002",
|
||||
"@dcloudio/webpack-uni-pages-loader": "^2.0.0-32920211122002",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"babel-plugin-import": "^1.11.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"jest": "^25.4.0",
|
||||
"mini-types": "*",
|
||||
"miniprogram-api-typings": "*",
|
||||
"postcss-class-rename": "^1.0.1",
|
||||
"postcss-comment": "^2.0.0",
|
||||
"postcss-windicss-postcss7": "^1.1.1",
|
||||
"sass": "^1.43.4",
|
||||
"sass-loader": "^8.0.2",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"browserslist": [
|
||||
"Android >= 4.4",
|
||||
"ios >= 9"
|
||||
],
|
||||
"uni-app": {
|
||||
"scripts": {}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
const path = require('path')
|
||||
module.exports = {
|
||||
parser: require('postcss-comment'),
|
||||
plugins: [
|
||||
require('postcss-windicss-postcss7')({}),
|
||||
require('postcss-import')({
|
||||
resolve (id, basedir, importOptions) {
|
||||
if (id.startsWith('/')) {
|
||||
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(3))
|
||||
} else if (id.startsWith('@/')) {
|
||||
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(2))
|
||||
} else if (id.startsWith('/') && !id.startsWith('//')) {
|
||||
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(1))
|
||||
}
|
||||
return id
|
||||
}
|
||||
}),
|
||||
require('autoprefixer')({
|
||||
remove: process.env.UNI_PLATFORM !== 'h5'
|
||||
}),
|
||||
require('@dcloudio/vue-cli-plugin-uni/packages/postcss'),
|
||||
// require('postcss-class-rename')({
|
||||
// [process.env.UNI_PLATFORM === 'mp-weixin' ? ':not\\\(\\\[hidden\\\]\\\)\\\s~\\\s:not\\\(\\\[hidden\\\]\\\)' : '不可能匹配的字符']: 'view + view', // 支持 space-x-4 语法
|
||||
// '\\\\:': '_', // 变体写法 focus_bg-primary
|
||||
// '\\\\/': '_', // 数值百分号 w-1_2
|
||||
// '\\\\.': '__', // 小数点写法 w-1__5
|
||||
// })
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>
|
||||
<%= htmlWebpackPlugin.options.title %>
|
||||
</title>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'
|
||||
})
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
|
||||
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
<link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>Please enable JavaScript to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
<script>
|
||||
// import { checkUpdate } from '@/utils/check-update';
|
||||
import { strToParams } from '@/utils/tools';
|
||||
import { wxMnpLogin } from '@/utils/login'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
clientid: null,
|
||||
};
|
||||
},
|
||||
onLaunch() {
|
||||
this.$store.dispatch('app/getConfit');
|
||||
|
||||
this.$store.dispatch('app/getSys');
|
||||
this.$store.dispatch('app/getArticle');
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
wxMnpLogin()
|
||||
/* #endif */
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
// const clientInfo = plus.push.getClientInfo()
|
||||
// console.log(clientInfo);
|
||||
// 锁定竖屏
|
||||
plus.screen.lockOrientation('portrait-primary');
|
||||
this.Network();
|
||||
this.uniPush();
|
||||
// checkUpdate();
|
||||
// #endif
|
||||
},
|
||||
onShow: function (option) {
|
||||
let invite_code = option.query.invite_code || strToParams(decodeURIComponent(option.query.scene)).invite_code
|
||||
uni.setStorageSync('INVITE_CODE',invite_code)
|
||||
},
|
||||
onHide: function () {
|
||||
console.log('App Hide');
|
||||
// this.bindCode
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 监听网络变化
|
||||
Network() {
|
||||
uni.onNetworkStatusChange((res) => {
|
||||
if (!res.isConnected) {
|
||||
// uni.showToast({
|
||||
// title: '网络已断开,请重新连接',
|
||||
// icon: 'none',
|
||||
// });
|
||||
}
|
||||
});
|
||||
},
|
||||
uniPush() {
|
||||
//监听push推送通知
|
||||
plus.push.addEventListener('receive', ({ type, title, content, payload }) => {
|
||||
console.log('======receive=====');
|
||||
console.log(payload);
|
||||
plus.push.setAutoNotification(true);
|
||||
if (type == 'receive' || uni.getSystemInfoSync().platform != 'ios') {
|
||||
//如果type!='receive'是自己本地插件的push消息栏,“拦截”避免死循环',安卓系统没有这个问题
|
||||
if (typeof payload != 'object') {
|
||||
payload = JSON.parse(payload);
|
||||
} //判断是否为object,不是的话手动转一下。hbuilderx 3.0以上版本已经修复此问题可省略
|
||||
plus.push.createMessage(content, JSON.stringify(payload), {
|
||||
title: payload.title,
|
||||
subtitle: payload.content,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//监听点击通知栏
|
||||
plus.push.addEventListener('click', ({ payload }) => {
|
||||
console.log('======push-click=====');
|
||||
console.log(payload);
|
||||
if (typeof payload != 'object') {
|
||||
payload = JSON.parse(payload);
|
||||
}
|
||||
if (!!payload.jump_link) {
|
||||
let pages = getCurrentPages();
|
||||
let currentWebview = pages[pages.length - 1].$getAppWebview();
|
||||
const currentRouterPath = `/${currentWebview.__uniapp_route}`;
|
||||
if (payload.jump_type == 1) {
|
||||
if (currentRouterPath != payload.jump_link) {
|
||||
this.$u.route(payload.jump_link);
|
||||
}
|
||||
} else if (payload.jump_type == 2) {
|
||||
this.$u.route(`/pages/web_view/index?url=${payload.jump_link}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@windicss;
|
||||
</style>
|
||||
<style lang="scss">
|
||||
@import 'uview-ui/index.scss';
|
||||
@import '@/style/index.scss';
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,565 @@
|
|||
let log = console.log; // 如果在项目的APP.vue文件中的onlaunch中设置 console.log = ()=> {} 则在此也不会有打印信息
|
||||
// log = ()=>{}; // 打开注释则该插件不会打印任何信息
|
||||
let _app = {
|
||||
lineFeedTags: ['<br />', '<br/>', '\r\n', '\n\t', '\r', '\n'], //换行符识别列表
|
||||
tagetLineFeedTag: `❤`, //将lineFeedTags列表中的字符串替换为该字符, 代表换行符, 只要找一个特殊符号就行,必须是单字符单字符单字符!
|
||||
//交互控制
|
||||
log(t) {
|
||||
log(t);
|
||||
}, // 打印控制,
|
||||
showLoading(msg, ifmask) {
|
||||
// uni.showLoading({
|
||||
// title: msg,
|
||||
// mask: ifmask || false
|
||||
// })
|
||||
},
|
||||
hideLoading() {
|
||||
// uni.hideLoading();
|
||||
},
|
||||
showToast(msg, icon) {
|
||||
// uni.showToast({
|
||||
// title: msg,
|
||||
// icon: icon || 'none'
|
||||
// })
|
||||
},
|
||||
getPosterUrl(objs) { // 后端获取背景图的url路径方法
|
||||
let {
|
||||
backgroundImage,
|
||||
type,
|
||||
formData
|
||||
} = objs;
|
||||
return new Promise((rs, rj) => {
|
||||
let image;
|
||||
if (backgroundImage) {
|
||||
image = backgroundImage;
|
||||
}else{
|
||||
switch (type) { //根据type获取背景图, 一般要改成request获取
|
||||
case 1:
|
||||
image = '';
|
||||
break;
|
||||
default:
|
||||
image = '/static/1.png';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (image) {
|
||||
rs(image); // resolve图片的路径
|
||||
}else{
|
||||
rj('背景图片路径不存在');
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//下面一般不用动他
|
||||
shareTypeListSheetArray: {
|
||||
array: [0, 1, 2, 3, 4, 5]
|
||||
}, // 分享类型 0-图文链接 1-纯文字 2-纯图片 3-音乐 4-视频 5-小程序
|
||||
isArray(arg) {
|
||||
return Object.prototype.toString.call(arg) === '[object Array]';
|
||||
},
|
||||
isObject(arg) {
|
||||
return Object.prototype.toString.call(arg) === '[object Object]';
|
||||
},
|
||||
isPromise(obj) {
|
||||
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
|
||||
},
|
||||
isNull(arg) {
|
||||
return arg === null;
|
||||
},
|
||||
isUndefined(arg) {
|
||||
return arg === undefined;
|
||||
},
|
||||
isUndef(arg) {
|
||||
return arg === undefined;
|
||||
},
|
||||
isNotNull_string(arg) {
|
||||
return arg !== null && arg !== undefined && arg !== '';
|
||||
},
|
||||
isFn(fn) {
|
||||
return fn && typeof fn === 'function';
|
||||
},
|
||||
getStorage(key, scb, fcb) {
|
||||
uni.getStorage({
|
||||
key,
|
||||
success: function(res) {
|
||||
if (res.data && res.data != "") {
|
||||
if (scb) scb(res.data);
|
||||
} else {
|
||||
if (fcb) fcb();
|
||||
}
|
||||
},
|
||||
fail: function() {
|
||||
if (fcb) fcb();
|
||||
}
|
||||
})
|
||||
},
|
||||
setStorage(key, data) {
|
||||
log('设置缓存')
|
||||
log('key:' + key)
|
||||
log('data:' + JSON.stringify(data));
|
||||
uni.setStorage({
|
||||
key,
|
||||
data
|
||||
})
|
||||
},
|
||||
setStorageSync(key, data) {
|
||||
uni.setStorageSync(key, data);
|
||||
},
|
||||
getStorageSync(key) {
|
||||
return uni.getStorageSync(key);
|
||||
},
|
||||
clearStorageSync() {
|
||||
uni.clearStorageSync();
|
||||
},
|
||||
removeStorageSync(key) {
|
||||
uni.removeStorageSync(key);
|
||||
},
|
||||
getImageInfo(url, cb, fcb) {
|
||||
url = checkMPUrl(url);
|
||||
uni.getImageInfo({
|
||||
src: url,
|
||||
success(res) {
|
||||
if (cb && typeof(cb) == 'function') cb(res);
|
||||
},
|
||||
fail(err) {
|
||||
if (fcb && typeof(fcb) == 'function') fcb(err);
|
||||
}
|
||||
})
|
||||
},
|
||||
downloadFile(url, cb) {
|
||||
url = checkMPUrl(url);
|
||||
uni.downloadFile({
|
||||
url,
|
||||
success(res) {
|
||||
if (cb && typeof(cb) == 'function') cb(res);
|
||||
}
|
||||
})
|
||||
},
|
||||
downloadFile_PromiseFc(url) {
|
||||
return new Promise((rs, rj) => {
|
||||
if (url.substring(0, 4) !== 'http') {
|
||||
rs(url);
|
||||
}else {
|
||||
url = checkMPUrl(url);
|
||||
log('url:' + url);
|
||||
|
||||
uni.downloadFile({
|
||||
url,
|
||||
success(res) {
|
||||
if (res && res.tempFilePath)
|
||||
rs(res.tempFilePath);
|
||||
else
|
||||
rj('not find tempFilePath');
|
||||
},
|
||||
fail(err) {
|
||||
log(err)
|
||||
rj(err);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
},
|
||||
saveFile(url) {
|
||||
uni.saveFile({
|
||||
tempFilePath: url,
|
||||
success(res) {
|
||||
log('保存成功:' + JSON.stringify(res))
|
||||
}
|
||||
})
|
||||
},
|
||||
downLoadAndSaveFile_PromiseFc(url) {
|
||||
return new Promise((rs, rj) => {
|
||||
log('准备下载并保存图片:' + url);
|
||||
if (url.substring(0, 4) === 'http') {
|
||||
url = checkMPUrl(url);
|
||||
uni.downloadFile({
|
||||
url,
|
||||
success(d_res) {
|
||||
log('下载背景图成功:' + JSON.stringify(d_res));
|
||||
if (d_res && d_res.tempFilePath) {
|
||||
|
||||
// #ifdef H5
|
||||
rs(d_res.tempFilePath);
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
uni.saveFile({
|
||||
tempFilePath: d_res.tempFilePath,
|
||||
success(s_res) {
|
||||
log('保存背景图成功:' + JSON.stringify(s_res));
|
||||
if (s_res && s_res.savedFilePath)
|
||||
rs(s_res.savedFilePath);
|
||||
else
|
||||
rs(d_res.tempFilePath);
|
||||
},
|
||||
fail(err) {
|
||||
rs(d_res.tempFilePath);
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
} else {
|
||||
rj('not find tempFilePath');
|
||||
}
|
||||
},
|
||||
fail(err) {
|
||||
rj(err);
|
||||
}
|
||||
})
|
||||
}else{
|
||||
rs(url);
|
||||
}
|
||||
})
|
||||
},
|
||||
checkFile_PromiseFc(url) {
|
||||
return new Promise((rs, rj) => {
|
||||
uni.getSavedFileList({
|
||||
success(res) {
|
||||
let d = res.fileList;
|
||||
let index = d.findIndex(item => {
|
||||
return item.filePath === url;
|
||||
})
|
||||
rs(index);
|
||||
},
|
||||
fail(err) {
|
||||
rj(err);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
removeSavedFile(path) {
|
||||
uni.getSavedFileList({
|
||||
success(res) {
|
||||
let d = res.fileList;
|
||||
let index = d.findIndex(item => {
|
||||
return item.filePath === path;
|
||||
});
|
||||
if (index >= 0)
|
||||
uni.removeSavedFile({
|
||||
filePath: path
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
fileNameInPath(path) {
|
||||
let s = path.split("/");
|
||||
return s[s.length - 1];
|
||||
},
|
||||
getImageInfo_PromiseFc(imgPath) {
|
||||
return new Promise((rs, rj) => {
|
||||
log('准备获取图片信息:' + imgPath);
|
||||
imgPath = checkMPUrl(imgPath);
|
||||
uni.getImageInfo({
|
||||
src: imgPath,
|
||||
success: res => {
|
||||
log('获取图片信息成功:' + JSON.stringify(res));
|
||||
rs(res);
|
||||
},
|
||||
fail: err => {
|
||||
log('获取图片信息失败:' + JSON.stringify(err));
|
||||
rj(err)
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
previewImage(urls) {
|
||||
if (typeof(urls) == 'string')
|
||||
urls = [urls];
|
||||
uni.previewImage({
|
||||
urls,
|
||||
})
|
||||
},
|
||||
actionSheet(obj, cb) {
|
||||
let sheetArray = [];
|
||||
for (let i = 0; i < obj.array.length; i++) {
|
||||
switch (obj.array[i]) {
|
||||
case 'sinaweibo':
|
||||
sheetArray[i] = '新浪微博';
|
||||
break;
|
||||
case 'qq':
|
||||
sheetArray[i] = 'QQ';
|
||||
break;
|
||||
case 'weixin':
|
||||
sheetArray[i] = '微信';
|
||||
break;
|
||||
case 'WXSceneSession':
|
||||
sheetArray[i] = '微信好友';
|
||||
break;
|
||||
case 'WXSenceTimeline':
|
||||
sheetArray[i] = '微信朋友圈';
|
||||
break;
|
||||
case 'WXSceneFavorite':
|
||||
sheetArray[i] = '微信收藏';
|
||||
break;
|
||||
case 0:
|
||||
sheetArray[i] = '图文链接';
|
||||
break;
|
||||
case 1:
|
||||
sheetArray[i] = '纯文字';
|
||||
break;
|
||||
case 2:
|
||||
sheetArray[i] = '纯图片';
|
||||
break;
|
||||
case 3:
|
||||
sheetArray[i] = '音乐';
|
||||
break;
|
||||
case 4:
|
||||
sheetArray[i] = '视频';
|
||||
break;
|
||||
case 5:
|
||||
sheetArray[i] = '小程序';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.showActionSheet(sheetArray, cb);
|
||||
},
|
||||
showActionSheet(sheetArray, cb) {
|
||||
uni.showActionSheet({
|
||||
itemList: sheetArray,
|
||||
success: (e) => {
|
||||
if (cb && typeof(cb) == 'function') cb(e.tapIndex);
|
||||
}
|
||||
})
|
||||
},
|
||||
getProvider(type, cb, sheet) {
|
||||
let _this = this;
|
||||
uni.getProvider({
|
||||
service: type,
|
||||
success: function(res) {
|
||||
if (sheet) {
|
||||
let obj = {};
|
||||
obj.array = res.provider;
|
||||
_this.actionSheet(obj, function(index) {
|
||||
if (cb && typeof(cb) == "function") cb(res.provider[index]);
|
||||
});
|
||||
} else {
|
||||
if (type == 'payment') {
|
||||
let providers = res.provider;
|
||||
let payTypeArray = [];
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
if (providers[i] == 'wxpay') {
|
||||
payTypeArray[i] = {
|
||||
name: '微信支付',
|
||||
value: providers[i],
|
||||
img: '/static/image/wei.png'
|
||||
};
|
||||
} else if (providers[i] == 'alipay') {
|
||||
payTypeArray[i] = {
|
||||
name: "支付宝支付",
|
||||
value: providers[i],
|
||||
img: '/static/image/ali.png'
|
||||
};
|
||||
}
|
||||
}
|
||||
if (cb && typeof(cb) == "function") cb(payTypeArray);
|
||||
} else {
|
||||
if (cb && typeof(cb) == "function") cb(res);
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
// #ifdef APP-PLUS
|
||||
getShare(providerName, WXScene, shareType, title, summary, href, imageUrl, miniProgramObj, mediaUrl, scb, fcb) { //miniProgram: {path: '', type: 0, webUrl: ''}
|
||||
let _this = this;
|
||||
if (typeof(shareType) == 'number' && !isNaN(shareType) && shareType >= 0) {
|
||||
_this.readyShare(providerName, WXScene, shareType, title, summary, href, imageUrl, miniProgramObj, mediaUrl, scb,
|
||||
fcb);
|
||||
} else {
|
||||
_this.actionSheet(_this.shareTypeListSheetArray, function(index) {
|
||||
_this.readyShare(providerName, WXScene, _this.shareTypeListSheetArray.array[index], title, summary, href,
|
||||
imageUrl, miniProgramObj, mediaUrl, scb, fcb);
|
||||
});
|
||||
}
|
||||
},
|
||||
readyShare(providerName, WXScene, shareType, title, summary, href, imageUrl, miniProgramObj, mediaUrl, scb, fcb) {
|
||||
let _this = this;
|
||||
let shareObjData = {};
|
||||
switch (shareType) {
|
||||
case 0:
|
||||
shareObjData = {
|
||||
href: href,
|
||||
summary: summary,
|
||||
imageUrl: imageUrl
|
||||
};
|
||||
break;
|
||||
case 1:
|
||||
shareObjData = {
|
||||
summary: summary,
|
||||
href: href
|
||||
};
|
||||
break;
|
||||
case 2:
|
||||
shareObjData = {
|
||||
imageUrl: imageUrl
|
||||
};
|
||||
break;
|
||||
case 3:
|
||||
if (mediaUrl) {
|
||||
_this.showToast('暂不支持此分享类型');
|
||||
return;
|
||||
};
|
||||
shareObjData = {
|
||||
mediaUrl: mediaUrl
|
||||
};
|
||||
break;
|
||||
case 4:
|
||||
if (mediaUrl) {
|
||||
_this.showToast('暂不支持此分享类型');
|
||||
return;
|
||||
};
|
||||
shareObjData = {
|
||||
mediaUrl: mediaUrl
|
||||
};
|
||||
break;
|
||||
case 5:
|
||||
shareObjData = {
|
||||
miniProgram: { ...miniProgramObj,
|
||||
id: miniProgramId,
|
||||
type: miniProgramShareType
|
||||
},
|
||||
imageUrl: imageUrl
|
||||
};
|
||||
providerName = 'weixin';
|
||||
break;
|
||||
default:
|
||||
_this.showToast('分享参数-shareType错误');
|
||||
return;
|
||||
break;
|
||||
}
|
||||
shareObjData.title = title;
|
||||
_this.share(providerName, WXScene, shareType, shareObjData, function(res) {
|
||||
if (scb && typeof(scb) == 'function') scb(res);
|
||||
}, function(err) {
|
||||
if (fcb && typeof(fcb) == 'function') fcb(err);
|
||||
});
|
||||
},
|
||||
share(providerName, WXScene, shareType, data, scb, fcb) {
|
||||
let _this = this;
|
||||
let shareObj = {
|
||||
provider: '',
|
||||
success: Function,
|
||||
fail: Function
|
||||
};
|
||||
if (providerName && providerName != '') {
|
||||
shareObj.provider = providerName;
|
||||
if (providerName == 'weixin') {
|
||||
_this.readyShareWXScene(WXScene, function(Scene) {
|
||||
shareObj.scene = Scene;
|
||||
_this.doingShare(shareObj, shareType, data, scb, fcb);
|
||||
});
|
||||
} else {
|
||||
_this.doingShare(shareObj, shareType, data, scb, fcb);
|
||||
}
|
||||
} else {
|
||||
_this.getProvider('share', function(name) {
|
||||
shareObj.provider = name;
|
||||
if (name == 'weixin') {
|
||||
_this.readyShareWXScene(WXScene, function(Scene) {
|
||||
shareObj.scene = Scene;
|
||||
_this.doingShare(shareObj, shareType, data, scb, fcb);
|
||||
});
|
||||
} else {
|
||||
_this.doingShare(shareObj, shareType, data, scb, fcb);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
},
|
||||
readyShareWXScene(WXScene, cb) {
|
||||
let _this = this;
|
||||
let WXScenetypelist = {
|
||||
array: ['WXSceneSession', 'WXSenceTimeline', 'WXSceneFavorite']
|
||||
};
|
||||
if (WXScene && WXScene != "") {
|
||||
if (cb && typeof(cb) == 'function') cb(WXScene);
|
||||
} else {
|
||||
_this.actionSheet(WXScenetypelist, function(index) {
|
||||
if (cb && typeof(cb) == 'function') cb(WXScenetypelist.array[index]);
|
||||
});
|
||||
}
|
||||
},
|
||||
doingShare(shareObj, shareType, data, scb, fcb) {
|
||||
shareObj.type = shareType;
|
||||
if (data && data.title) shareObj.title = data.title;
|
||||
switch (shareType) {
|
||||
case 0: //图文链接
|
||||
shareObj.href = data.href;
|
||||
shareObj.summary = data.summary;
|
||||
shareObj.imageUrl = data.imageUrl;
|
||||
break;
|
||||
case 1: //纯文字
|
||||
shareObj.summary = data.summary;
|
||||
shareObj.href = data.href;
|
||||
break;
|
||||
case 2: //纯图片
|
||||
shareObj.imageUrl = data.imageUrl;
|
||||
break;
|
||||
case 3: //音乐
|
||||
if (!data.mediaUrl) {
|
||||
_this.showToast('暂不支持此分享类型');
|
||||
return;
|
||||
};
|
||||
shareObj.mediaUrl = data.mediaUrl;
|
||||
break;
|
||||
case 4: //视频
|
||||
if (!data.mediaUrl) {
|
||||
_this.showToast('暂不支持此分享类型');
|
||||
return;
|
||||
};
|
||||
shareObj.mediaUrl = data.mediaUrl;
|
||||
break;
|
||||
case 5: //小程序
|
||||
if (miniProgramId == '') {
|
||||
uni.showToast('未设置小程序ID, 请使用其他方式分享');
|
||||
return;
|
||||
}
|
||||
let mp = {
|
||||
id: miniProgramId
|
||||
};
|
||||
mp.path = data.miniProgram.path;
|
||||
mp.type = data.miniProgram.type;
|
||||
if (data.miniProgram.webUrl) mp.webUrl = data.miniProgram.webUrl;
|
||||
shareObj.miniProgram = mp;
|
||||
shareObj.imageUrl = data.imageUrl;
|
||||
break;
|
||||
default:
|
||||
appJS.showToast('分享参数-shareType错误');
|
||||
break;
|
||||
}
|
||||
shareObj.success = function(res) {
|
||||
if (scb && typeof(scb) == 'function') scb(res);
|
||||
}
|
||||
shareObj.fail = function(err) {
|
||||
if (fcb && typeof(fcb) == 'function') fcb(err);
|
||||
}
|
||||
log(JSON.stringify(shareObj));
|
||||
uni.share(shareObj);
|
||||
},
|
||||
// #endif
|
||||
}
|
||||
|
||||
function checkMPUrl(url) {
|
||||
// #ifdef MP
|
||||
// if(process.env.NODE_ENV !== 'development'){
|
||||
// if(
|
||||
// url.substring(0, 4) === 'http' &&
|
||||
// url.substring(0, 5) !== 'https' &&
|
||||
// url.substring(0, 12) !== 'http://store' &&
|
||||
// url.substring(0, 10) !== 'http://tmp' &&
|
||||
// url.substring(0, 10) !== 'http://usr'
|
||||
// ) {
|
||||
// url = 'https' + url.substring(4, url.length);
|
||||
// }
|
||||
// }
|
||||
// #endif
|
||||
return url;
|
||||
}
|
||||
|
||||
module.exports = _app;
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
function getLocalFilePath(path) {
|
||||
if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path.indexOf('_downloads') === 0) {
|
||||
return path
|
||||
}
|
||||
if (path.indexOf('file://') === 0) {
|
||||
return path
|
||||
}
|
||||
if (path.indexOf('/storage/emulated/0/') === 0) {
|
||||
return path
|
||||
}
|
||||
if (path.indexOf('/') === 0) {
|
||||
var localFilePath = plus.io.convertAbsoluteFileSystem(path)
|
||||
if (localFilePath !== path) {
|
||||
return localFilePath
|
||||
} else {
|
||||
path = path.substr(1)
|
||||
}
|
||||
}
|
||||
return '_www/' + path
|
||||
}
|
||||
|
||||
export function pathToBase64(path) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (typeof window === 'object' && 'document' in window) {
|
||||
if (typeof FileReader === 'function') {
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr.open('GET', path, true)
|
||||
xhr.responseType = 'blob'
|
||||
xhr.onload = function() {
|
||||
if (this.status === 200) {
|
||||
let fileReader = new FileReader()
|
||||
fileReader.onload = function(e) {
|
||||
resolve(e.target.result)
|
||||
}
|
||||
fileReader.onerror = reject
|
||||
fileReader.readAsDataURL(this.response)
|
||||
}
|
||||
}
|
||||
xhr.onerror = reject
|
||||
xhr.send()
|
||||
return
|
||||
}
|
||||
var canvas = document.createElement('canvas')
|
||||
var c2x = canvas.getContext('2d')
|
||||
var img = new Image
|
||||
img.onload = function() {
|
||||
canvas.width = img.width
|
||||
canvas.height = img.height
|
||||
c2x.drawImage(img, 0, 0)
|
||||
resolve(canvas.toDataURL())
|
||||
canvas.height = canvas.width = 0
|
||||
}
|
||||
img.onerror = reject
|
||||
img.src = path
|
||||
return
|
||||
}
|
||||
if (typeof plus === 'object') {
|
||||
plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), function(entry) {
|
||||
entry.file(function(file) {
|
||||
var fileReader = new plus.io.FileReader()
|
||||
fileReader.onload = function(data) {
|
||||
resolve(data.target.result)
|
||||
}
|
||||
fileReader.onerror = function(error) {
|
||||
reject(error)
|
||||
}
|
||||
fileReader.readAsDataURL(file)
|
||||
}, function(error) {
|
||||
reject(error)
|
||||
})
|
||||
}, function(error) {
|
||||
reject(error)
|
||||
})
|
||||
return
|
||||
}
|
||||
if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
|
||||
wx.getFileSystemManager().readFile({
|
||||
filePath: path,
|
||||
encoding: 'base64',
|
||||
success: function(res) {
|
||||
resolve('data:image/png;base64,' + res.data)
|
||||
},
|
||||
fail: function(error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
reject(new Error('not support'))
|
||||
})
|
||||
}
|
||||
|
||||
export function base64ToPath(base64) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (typeof window === 'object' && 'document' in window) {
|
||||
base64 = base64.split(',')
|
||||
var type = base64[0].match(/:(.*?);/)[1]
|
||||
var str = atob(base64[1])
|
||||
var n = str.length
|
||||
var array = new Uint8Array(n)
|
||||
while (n--) {
|
||||
array[n] = str.charCodeAt(n)
|
||||
}
|
||||
return resolve((window.URL || window.webkitURL).createObjectURL(new Blob([array], { type: type })))
|
||||
}
|
||||
var extName = base64.match(/data\:\S+\/(\S+);/)
|
||||
if (extName) {
|
||||
extName = extName[1]
|
||||
} else {
|
||||
reject(new Error('base64 error'))
|
||||
}
|
||||
var fileName = Date.now() + '.' + extName
|
||||
if (typeof plus === 'object') {
|
||||
var bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
|
||||
bitmap.loadBase64Data(base64, function() {
|
||||
var filePath = '_doc/uniapp_temp/' + fileName
|
||||
bitmap.save(filePath, {}, function() {
|
||||
bitmap.clear()
|
||||
resolve(filePath)
|
||||
}, function(error) {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
}, function(error) {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
return
|
||||
}
|
||||
if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
|
||||
var filePath = wx.env.USER_DATA_PATH + '/' + fileName
|
||||
wx.getFileSystemManager().writeFile({
|
||||
filePath: filePath,
|
||||
data: base64.replace(/^data:\S+\/\S+;base64,/, ''),
|
||||
encoding: 'base64',
|
||||
success: function() {
|
||||
resolve(filePath)
|
||||
},
|
||||
fail: function(error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
reject(new Error('not support'))
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<view class="px-41rpx ">
|
||||
<view :class="index==length-1?'lastChild':''" class="flex pt-base border-b border-txBorder pb-base ">
|
||||
<u-avatar size="68" src="https://img.js.design/assets/img/6195a4af14cabb34f72301a8.png"></u-avatar>
|
||||
<view class="flex-1 ml-10rpx">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="text-txBase text-md">小道道</view>
|
||||
<view class="flex items-center">
|
||||
<image class="w-32rpx h-32rpx" src="/static/images/user/smile.png" mode=""></image>
|
||||
<view class="text-md text-bgSubtitle ml-6rpx">好评</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-sm text-txGray">2021.07.29 </view>
|
||||
<view class="text-txBase text-md">东西收到了,到货很快,很满意效果很好!东西收到了,到货很快,很满意效果很好!东西收到了,到货很快,很满意效果很好!</view>
|
||||
<view class="bg-txBorder px-base pt-8rpx mt-10rpx">
|
||||
<view class="text-md text-txGray">商家恢复:感谢亲亲的支持 ~商家恢复:感谢亲亲的支持 ~商家恢复:感谢亲亲的支持 ~商家恢复:感谢亲亲的支持 ~</view>
|
||||
<view class="text-right text-xs text-txGray pb-15rpx">2021.07.29 </view>
|
||||
</view>
|
||||
<view class="flex items-center flex-wrap">
|
||||
<block v-for="(item,index) in 3" :key="index">
|
||||
<u-image class="mr-base mt-base" width="160rpx" height="160rpx" src="https://img.js.design/assets/img/6195a4af14cabb34f72301a8.png"></u-image>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name:"comment",
|
||||
props:{
|
||||
index:{
|
||||
type:Number,
|
||||
default:1
|
||||
},
|
||||
length:{
|
||||
type:Number,
|
||||
default:0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.lastChild{
|
||||
border:0
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<view class="px-20rpx coupon" :class="{disable:disable}">
|
||||
<view class="mt-20rpx" v-for="item in list" :key="item.id" @tap.stop="onClick(item)">
|
||||
<view class="coupon-item flex items-center text-hex-BA4607">
|
||||
<view class="w-200rpx text-white text-center flex-none">
|
||||
<block v-if="item.type==1">
|
||||
<price-format :color="!disable?'':'#A6A6A6'" :first-size="60" :second-size="50" :subscript-size="34"
|
||||
:price="item.amount" :weight="500" />
|
||||
</block>
|
||||
<block v-else>
|
||||
<view class="text-60rpx after">{{item.amount}}</view>
|
||||
</block>
|
||||
</view>
|
||||
<view class="ml-20rpx flex-1 w-0">
|
||||
<view class="text-36rpx">{{item.name}}</view>
|
||||
<view class=" line-1 font-medium text-28rpx mt-20rpx">{{item.threshold>0?`满${item.threshold}可用`:'无门槛'}}</view>
|
||||
<view class="text-24rpx text-h666 mt-20rpx">有效期至:{{item.use_end_at}}</view>
|
||||
</view>
|
||||
<view v-if="checkout" class="flex-none">
|
||||
<view class="px-20rpx" @tap.prevent.stop="onClick(item)">
|
||||
<image v-if="item.id == val" src="/static/images/coupon/select-red.png" class="w-40rpx h-40rpx"></image>
|
||||
<image v-else src="/static/images/coupon/unselect.png" class="w-40rpx h-40rpx"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
value: {
|
||||
type: [Number, String]
|
||||
},
|
||||
checkout: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
disable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
val: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(e) {
|
||||
return this.$emit('input', e)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick(e) {
|
||||
if (e.id == this.val) this.val = ''
|
||||
else this.val = e.id
|
||||
return this.$emit('change', this.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.coupon-item {
|
||||
position: relative;
|
||||
height: 200rpx;
|
||||
background-image: url('/static/images/coupon/coupon.png');
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.disable {
|
||||
.coupon-item {
|
||||
background-image: url('/static/images/coupon/coupon_no.png');
|
||||
@apply text-hex-A6A6A6
|
||||
}
|
||||
}
|
||||
|
||||
.after {
|
||||
&::after {
|
||||
content: '折';
|
||||
font-size: 60%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
<template>
|
||||
<view class="u-avatar" :style="[wrapStyle]" @tap="click">
|
||||
<image
|
||||
@error="loadError"
|
||||
:style="[imgStyle]"
|
||||
class="u-avatar__img"
|
||||
v-if="!uText && avatar"
|
||||
:src="avatar"
|
||||
:mode="imgMode"
|
||||
></image>
|
||||
<text class="u-line-1" v-else-if="uText" :style="{
|
||||
fontSize: '38rpx'
|
||||
}">{{uText}}</text>
|
||||
<slot v-else></slot>
|
||||
<view class="u-avatar__sex" v-if="showSex" :class="['u-avatar__sex--' + sexIcon]" :style="[uSexStyle]">
|
||||
<u-icon :name="sexIcon" size="20"></u-icon>
|
||||
</view>
|
||||
<view class="u-avatar__level" v-if="showLevel" :style="[uLevelStyle]">
|
||||
<u-icon :name="levelIcon" size="20"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 本地默认图片
|
||||
let base64Avatar=require('../../static/images/user/avatar.png');
|
||||
/**
|
||||
* avatar 头像
|
||||
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
|
||||
* @tutorial https://www.uviewui.com/components/avatar.html
|
||||
* @property {String} bg-color 背景颜色,一般显示文字时用(默认#ffffff)
|
||||
* @property {String} src 头像路径,如加载失败,将会显示默认头像
|
||||
* @property {String Number} size 头像尺寸,可以为指定字符串(large, default, mini),或者数值,单位rpx(默认default)
|
||||
* @property {String} mode 显示类型,见上方说明(默认circle)
|
||||
* @property {String} sex-icon 性别图标,man-男,woman-女(默认man)
|
||||
* @property {String} level-icon 等级图标(默认level)
|
||||
* @property {String} sex-bg-color 性别图标背景颜色
|
||||
* @property {String} level-bg-color 等级图标背景颜色
|
||||
* @property {String} show-sex 是否显示性别图标(默认false)
|
||||
* @property {String} show-level 是否显示等级图标(默认false)
|
||||
* @property {String} img-mode 头像图片的裁剪类型,与uni的image组件的mode参数一致,如效果达不到需求,可尝试传widthFix值(默认aspectFill)
|
||||
* @property {String} index 用户传递的标识符值,如果是列表循环,可穿v-for的index值
|
||||
* @event {Function} click 头像被点击
|
||||
* @example <u-avatar :src="src"></u-avatar>
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'u-avatar',
|
||||
props: {
|
||||
// 背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: 'transparent'
|
||||
},
|
||||
// 头像路径
|
||||
src: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 尺寸,large-大,default-中等,mini-小,如果为数值,则单位为rpx
|
||||
// 宽度等于高度
|
||||
size: {
|
||||
type: [String, Number],
|
||||
default: 'default'
|
||||
},
|
||||
// 头像模型,square-带圆角方形,circle-圆形
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'circle'
|
||||
},
|
||||
// 文字内容
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 图片的裁剪模型
|
||||
imgMode: {
|
||||
type: String,
|
||||
default: 'aspectFill'
|
||||
},
|
||||
// 标识符
|
||||
index: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
// 右上角性别角标,man-男,woman-女
|
||||
sexIcon: {
|
||||
type: String,
|
||||
default: 'man'
|
||||
},
|
||||
// 右下角的等级图标
|
||||
levelIcon: {
|
||||
type: String,
|
||||
default: 'level'
|
||||
},
|
||||
// 右下角等级图标背景颜色
|
||||
levelBgColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 右上角性别图标的背景颜色
|
||||
sexBgColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 是否显示性别图标
|
||||
showSex: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否显示等级图标
|
||||
showLevel: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
error: false,
|
||||
// 头像的地址,因为如果加载错误,需要赋值为默认图片,props值无法修改,所以需要一个中间值
|
||||
avatar: this.src ? this.src : base64Avatar,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
src(n) {
|
||||
// 用户可能会在头像加载失败时,再次修改头像值,所以需要重新赋值
|
||||
if(!n) {
|
||||
// 如果传入null或者'',或者undefined,显示默认头像
|
||||
this.avatar = base64Avatar;
|
||||
this.error = true;
|
||||
} else {
|
||||
this.avatar = n;
|
||||
this.error = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
wrapStyle() {
|
||||
let style = {};
|
||||
style.height = this.size == 'large' ? '120rpx' : this.size == 'default' ?
|
||||
'90rpx' : this.size == 'mini' ? '70rpx' : this.size + 'rpx';
|
||||
style.width = style.height;
|
||||
style.flex = `0 0 ${style.height}`;
|
||||
style.backgroundColor = this.bgColor;
|
||||
style.borderRadius = this.mode == 'circle' ? '500px' : '5px';
|
||||
if(this.text) style.padding = `0 6rpx`;
|
||||
return style;
|
||||
},
|
||||
imgStyle() {
|
||||
let style = {};
|
||||
style.borderRadius = this.mode == 'circle' ? '500px' : '5px';
|
||||
return style;
|
||||
},
|
||||
// 取字符串的第一个字符
|
||||
uText() {
|
||||
return String(this.text)[0];
|
||||
},
|
||||
// 性别图标的自定义样式
|
||||
uSexStyle() {
|
||||
let style = {};
|
||||
if(this.sexBgColor) style.backgroundColor = this.sexBgColor;
|
||||
return style;
|
||||
},
|
||||
// 等级图标的自定义样式
|
||||
uLevelStyle() {
|
||||
let style = {};
|
||||
if(this.levelBgColor) style.backgroundColor = this.levelBgColor;
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 图片加载错误时,显示默认头像
|
||||
loadError() {
|
||||
this.error = true;
|
||||
this.avatar = base64Avatar;
|
||||
},
|
||||
click() {
|
||||
this.$emit('click', this.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "uview-ui/libs/css/style.components.scss";
|
||||
|
||||
.u-avatar {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: inline-flex;
|
||||
/* #endif */
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
color: $u-content-color;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
|
||||
&__img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__sex {
|
||||
position: absolute;
|
||||
width: 32rpx;
|
||||
color: #ffffff;
|
||||
height: 32rpx;
|
||||
@include vue-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 100rpx;
|
||||
top: 5%;
|
||||
z-index: 1;
|
||||
right: -7%;
|
||||
border: 1px #ffffff solid;
|
||||
|
||||
&--man {
|
||||
background-color: $u-type-primary;
|
||||
}
|
||||
|
||||
&--woman {
|
||||
background-color: $u-type-error;
|
||||
}
|
||||
|
||||
&--none {
|
||||
background-color: $u-type-warning;
|
||||
}
|
||||
}
|
||||
|
||||
&__level {
|
||||
position: absolute;
|
||||
width: 32rpx;
|
||||
color: #ffffff;
|
||||
height: 32rpx;
|
||||
@include vue-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 100rpx;
|
||||
bottom: 5%;
|
||||
z-index: 1;
|
||||
right: -7%;
|
||||
border: 1px #ffffff solid;
|
||||
background-color: $u-type-warning;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,437 @@
|
|||
<template>
|
||||
<view class="u-form-item" :class="[className,{'u-border-bottom': elBorderBottom, 'u-form-item__border-bottom--error': validateState === 'error' && showError('border-bottom')}]">
|
||||
<view class="u-form-item__body" :style="{
|
||||
flexDirection: elLabelPosition == 'left' ? 'row' : 'column'
|
||||
}">
|
||||
<!-- 微信小程序中,将一个参数设置空字符串,结果会变成字符串"true" -->
|
||||
<view class="u-form-item--left" :style="{
|
||||
width: uLabelWidth,
|
||||
flex: `0 0 ${uLabelWidth}`,
|
||||
marginBottom: elLabelPosition == 'left' ? 0 : '10rpx',
|
||||
}">
|
||||
<!-- 为了块对齐 -->
|
||||
<view class="u-form-item--left__content" v-if="required || leftIcon || label">
|
||||
<!-- nvue不支持伪元素before -->
|
||||
<text v-if="required" class="u-form-item--left__content--required">*</text>
|
||||
<view class="u-form-item--left__content__icon" v-if="leftIcon || $slots.left">
|
||||
<slot name="left">
|
||||
<u-icon :name="leftIcon" :custom-style="leftIconStyle"></u-icon>
|
||||
</slot>
|
||||
</view>
|
||||
<view class="u-form-item--left__content__label" :style="[elLabelStyle, {
|
||||
'justify-content': elLabelAlign == 'left' ? 'flex-start' : elLabelAlign == 'center' ? 'center' : 'flex-end'
|
||||
}]">
|
||||
{{label}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-form-item--right u-flex">
|
||||
<view class="u-form-item--right__content">
|
||||
<view class="u-form-item--right__content__slot ">
|
||||
<slot />
|
||||
</view>
|
||||
<view class="u-form-item--right__content__icon u-flex" v-if="$slots.right || rightIcon">
|
||||
<u-icon :custom-style="rightIconStyle" v-if="rightIcon" :name="rightIcon"></u-icon>
|
||||
<slot name="right" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-form-item__message" v-if="validateState === 'error' && showError('message')" :style="{
|
||||
paddingLeft: elLabelPosition == 'left' ? $u.addUnit(elLabelWidth) : '0',
|
||||
}">{{validateMessage}}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Emitter from 'uview-ui/libs/util/emitter.js';
|
||||
import schema from 'uview-ui/libs/util/async-validator';
|
||||
// 去除警告信息
|
||||
schema.warning = function() {};
|
||||
|
||||
/**
|
||||
* form-item 表单item
|
||||
* @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。
|
||||
* @tutorial http://uviewui.com/components/form.html
|
||||
* @property {String} label 左侧提示文字
|
||||
* @property {Object} prop 表单域model对象的属性名,在使用 validate、resetFields 方法的情况下,该属性是必填的
|
||||
* @property {Boolean} border-bottom 是否显示表单域的下划线边框
|
||||
* @property {String} label-position 表单域提示文字的位置,left-左侧,top-上方
|
||||
* @property {String Number} label-width 提示文字的宽度,单位rpx(默认90)
|
||||
* @property {Object} label-style lable的样式,对象形式
|
||||
* @property {String} label-align lable的对齐方式
|
||||
* @property {String} right-icon 右侧自定义字体图标(限uView内置图标)或图片地址
|
||||
* @property {String} left-icon 左侧自定义字体图标(限uView内置图标)或图片地址
|
||||
* @property {Object} left-icon-style 左侧图标的样式,对象形式
|
||||
* @property {Object} right-icon-style 右侧图标的样式,对象形式
|
||||
* @property {Boolean} required 是否显示左边的"*"号,这里仅起展示作用,如需校验必填,请通过rules配置必填规则(默认false)
|
||||
* @example <u-form-item label="姓名"><u-input v-model="form.name" /></u-form-item>
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'u-form-item',
|
||||
mixins: [Emitter],
|
||||
inject: {
|
||||
uForm: {
|
||||
default () {
|
||||
return null
|
||||
}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
className:{
|
||||
default:'bgUserInput rounded-full'
|
||||
},
|
||||
// input的label提示语
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 绑定的值
|
||||
prop: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 是否显示表单域的下划线边框
|
||||
borderBottom: {
|
||||
type: [String, Boolean],
|
||||
default: ''
|
||||
},
|
||||
// label的位置,left-左边,top-上边
|
||||
labelPosition: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// label的宽度,单位rpx
|
||||
labelWidth: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
// lable的样式,对象形式
|
||||
labelStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// lable字体的对齐方式
|
||||
labelAlign: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 右侧图标
|
||||
rightIcon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 左侧图标
|
||||
leftIcon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 左侧图标的样式
|
||||
leftIconStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 左侧图标的样式
|
||||
rightIconStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 是否显示左边的必填星号,只作显示用,具体校验必填的逻辑,请在rules中配置
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialValue: '', // 存储的默认值
|
||||
// isRequired: false, // 是否必填,由于人性化考虑,必填"*"号通过props的required配置,不再通过rules的规则自动生成
|
||||
validateState: '', // 是否校验成功
|
||||
validateMessage: '', // 校验失败的提示语
|
||||
// 有错误时的提示方式,message-提示信息,border-如果input设置了边框,变成呈红色,
|
||||
errorType: ['message'],
|
||||
fieldValue: '', // 获取当前子组件input的输入的值
|
||||
// 父组件的参数,在computed计算中,无法得知this.parent发生变化,故将父组件的参数值,放到data中
|
||||
parentData: {
|
||||
borderBottom: true,
|
||||
labelWidth: 90,
|
||||
labelPosition: 'left',
|
||||
labelStyle: {},
|
||||
labelAlign: 'left',
|
||||
}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
validateState(val) {
|
||||
this.broadcastInputError();
|
||||
},
|
||||
// 监听u-form组件的errorType的变化
|
||||
"uForm.errorType"(val) {
|
||||
this.errorType = val;
|
||||
this.broadcastInputError();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
// 计算后的label宽度,由于需要多个判断,故放到computed中
|
||||
uLabelWidth() {
|
||||
// 如果用户设置label为空字符串(微信小程序空字符串最终会变成字符串的'true'),意味着要将label的位置宽度设置为auto
|
||||
return this.elLabelPosition == 'left' ? (this.label === 'true' || this.label === '' ? 'auto' : this.$u.addUnit(this
|
||||
.elLabelWidth)) : '100%';
|
||||
},
|
||||
showError() {
|
||||
return type => {
|
||||
// 如果errorType数组中含有none,或者toast提示类型
|
||||
if (this.errorType.indexOf('none') >= 0) return false;
|
||||
else if (this.errorType.indexOf(type) >= 0) return true;
|
||||
else return false;
|
||||
}
|
||||
},
|
||||
// label的宽度
|
||||
elLabelWidth() {
|
||||
// label默认宽度为90,优先使用本组件的值,如果没有(如果设置为0,也算是配置了值,依然起效),则用u-form的值
|
||||
return (this.labelWidth != 0 || this.labelWidth != '') ? this.labelWidth : (this.parentData.labelWidth ? this.parentData
|
||||
.labelWidth :
|
||||
90);
|
||||
},
|
||||
// label的样式
|
||||
elLabelStyle() {
|
||||
return Object.keys(this.labelStyle).length ? this.labelStyle : (this.parentData.labelStyle ? this.parentData.labelStyle :
|
||||
{});
|
||||
},
|
||||
// label的位置,左侧或者上方
|
||||
elLabelPosition() {
|
||||
return this.labelPosition ? this.labelPosition : (this.parentData.labelPosition ? this.parentData.labelPosition :
|
||||
'left');
|
||||
},
|
||||
// label的对齐方式
|
||||
elLabelAlign() {
|
||||
return this.labelAlign ? this.labelAlign : (this.parentData.labelAlign ? this.parentData.labelAlign : 'left');
|
||||
},
|
||||
// label的下划线
|
||||
elBorderBottom() {
|
||||
// 子组件的borderBottom默认为空字符串,如果不等于空字符串,意味着子组件设置了值,优先使用子组件的值
|
||||
return this.borderBottom !== '' ? this.borderBottom : this.parentData.borderBottom ? this.parentData.borderBottom :
|
||||
true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
broadcastInputError() {
|
||||
// 子组件发出事件,第三个参数为true或者false,true代表有错误
|
||||
this.broadcast('u-input', 'on-form-item-error', this.validateState === 'error' && this.showError('border'));
|
||||
},
|
||||
// 判断是否需要required校验
|
||||
setRules() {
|
||||
let that = this;
|
||||
// 由于人性化考虑,必填"*"号通过props的required配置,不再通过rules的规则自动生成
|
||||
// 从父组件u-form拿到当前u-form-item需要验证 的规则
|
||||
// let rules = this.getRules();
|
||||
// if (rules.length) {
|
||||
// this.isRequired = rules.some(rule => {
|
||||
// // 如果有必填项,就返回,没有的话,就是undefined
|
||||
// return rule.required;
|
||||
// });
|
||||
// }
|
||||
|
||||
// blur事件
|
||||
this.$on('on-form-blur', that.onFieldBlur);
|
||||
// change事件
|
||||
this.$on('on-form-change', that.onFieldChange);
|
||||
},
|
||||
|
||||
// 从u-form的rules属性中,取出当前u-form-item的校验规则
|
||||
getRules() {
|
||||
// 父组件的所有规则
|
||||
let rules = this.parent.rules;
|
||||
rules = rules ? rules[this.prop] : [];
|
||||
// 保证返回的是一个数组形式
|
||||
return [].concat(rules || []);
|
||||
},
|
||||
|
||||
// blur事件时进行表单校验
|
||||
onFieldBlur() {
|
||||
this.validation('blur');
|
||||
},
|
||||
|
||||
// change事件进行表单校验
|
||||
onFieldChange() {
|
||||
this.validation('change');
|
||||
},
|
||||
|
||||
// 过滤出符合要求的rule规则
|
||||
getFilteredRule(triggerType = '') {
|
||||
let rules = this.getRules();
|
||||
// 整体验证表单时,triggerType为空字符串,此时返回所有规则进行验证
|
||||
if (!triggerType) return rules;
|
||||
// 历遍判断规则是否有对应的事件,比如blur,change触发等的事件
|
||||
// 使用indexOf判断,是因为某些时候设置的验证规则的trigger属性可能为多个,比如['blur','change']
|
||||
// 某些场景可能的判断规则,可能不存在trigger属性,故先判断是否存在此属性
|
||||
return rules.filter(res => res.trigger && res.trigger.indexOf(triggerType) !== -1);
|
||||
},
|
||||
|
||||
// 校验数据
|
||||
validation(trigger, callback = () => {}) {
|
||||
// 检验之间,先获取需要校验的值
|
||||
this.fieldValue = this.parent.model[this.prop];
|
||||
// blur和change是否有当前方式的校验规则
|
||||
let rules = this.getFilteredRule(trigger);
|
||||
// 判断是否有验证规则,如果没有规则,也调用回调方法,否则父组件u-form会因为
|
||||
// 对count变量的统计错误而无法进入上一层的回调
|
||||
if (!rules || rules.length === 0) {
|
||||
return callback('');
|
||||
}
|
||||
// 设置当前的装填,标识为校验中
|
||||
this.validateState = 'validating';
|
||||
// 调用async-validator的方法
|
||||
let validator = new schema({
|
||||
[this.prop]: rules
|
||||
});
|
||||
validator.validate({
|
||||
[this.prop]: this.fieldValue
|
||||
}, {
|
||||
firstFields: true
|
||||
}, (errors, fields) => {
|
||||
// 记录状态和报错信息
|
||||
this.validateState = !errors ? 'success' : 'error';
|
||||
this.validateMessage = errors ? errors[0].message : '';
|
||||
// 调用回调方法
|
||||
callback(this.validateMessage);
|
||||
});
|
||||
},
|
||||
|
||||
// 清空当前的u-form-item
|
||||
resetField() {
|
||||
this.parent.model[this.prop] = this.initialValue;
|
||||
// 设置为`success`状态,只是为了清空错误标记
|
||||
this.validateState = 'success';
|
||||
}
|
||||
},
|
||||
|
||||
// 组件创建完成时,将当前实例保存到u-form中
|
||||
mounted() {
|
||||
// 支付宝、头条小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环应用
|
||||
this.parent = this.$u.$parent.call(this, 'u-form');
|
||||
if (this.parent) {
|
||||
// 历遍parentData中的属性,将parent中的同名属性赋值给parentData
|
||||
Object.keys(this.parentData).map(key => {
|
||||
this.parentData[key] = this.parent[key];
|
||||
});
|
||||
// 如果没有传入prop,或者uForm为空(如果u-form-input单独使用,就不会有uForm注入),就不进行校验
|
||||
if (this.prop) {
|
||||
// 将本实例添加到父组件中
|
||||
this.parent.fields.push(this);
|
||||
this.errorType = this.parent.errorType;
|
||||
// 设置初始值
|
||||
this.initialValue = this.fieldValue;
|
||||
// 添加表单校验,这里必须要写在$nextTick中,因为u-form的rules是通过ref手动传入的
|
||||
// 不在$nextTick中的话,可能会造成执行此处代码时,父组件还没通过ref把规则给u-form,导致规则为空
|
||||
this.$nextTick(() => {
|
||||
this.setRules();
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 组件销毁前,将实例从u-form的缓存中移除
|
||||
beforeDestroy() {
|
||||
// 如果当前没有prop的话表示当前不要进行删除(因为没有注入)
|
||||
if (this.parent && this.prop) {
|
||||
this.parent.fields.map((item, index) => {
|
||||
if (item === this) this.parent.fields.splice(index, 1);
|
||||
})
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "uview-ui/libs/css/style.components.scss";
|
||||
|
||||
.u-form-item {
|
||||
@include vue-flex;
|
||||
// align-items: flex-start;
|
||||
font-size: 28rpx;
|
||||
color: $u-main-color;
|
||||
box-sizing: border-box;
|
||||
line-height: $u-form-item-height;
|
||||
flex-direction: column;
|
||||
|
||||
&__border-bottom--error:after {
|
||||
border-color: $u-type-error;
|
||||
}
|
||||
|
||||
&__body {
|
||||
@include vue-flex;
|
||||
}
|
||||
|
||||
&--left {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
|
||||
&__content {
|
||||
position: relative;
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
padding-right: 10rpx;
|
||||
flex: 1;
|
||||
|
||||
&__icon {
|
||||
margin-right: 8rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&--required {
|
||||
position: absolute;
|
||||
left: -16rpx;
|
||||
vertical-align: middle;
|
||||
color: $u-type-error;
|
||||
padding-top: 6rpx;
|
||||
}
|
||||
|
||||
&__label {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--right {
|
||||
flex: 1;
|
||||
|
||||
&__content {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
&__slot {
|
||||
flex: 1;
|
||||
/* #ifndef MP */
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
&__icon {
|
||||
margin-left: 10rpx;
|
||||
color: $u-light-color;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__message {
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
color: $u-type-error;
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
<template>
|
||||
<view class="u-form"><slot /></view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* form 表单
|
||||
* @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。
|
||||
* @tutorial http://uviewui.com/components/form.html
|
||||
* @property {Object} model 表单数据对象
|
||||
* @property {Boolean} border-bottom 是否显示表单域的下划线边框
|
||||
* @property {String} label-position 表单域提示文字的位置,left-左侧,top-上方
|
||||
* @property {String Number} label-width 提示文字的宽度,单位rpx(默认90)
|
||||
* @property {Object} label-style lable的样式,对象形式
|
||||
* @property {String} label-align lable的对齐方式
|
||||
* @property {Object} rules 通过ref设置,见官网说明
|
||||
* @property {Array} error-type 错误的提示方式,数组形式,见上方说明(默认['message'])
|
||||
* @example <u-form :model="form" ref="uForm"></u-form>
|
||||
*/
|
||||
import Schema from '@/utils/async-validator';
|
||||
export default {
|
||||
name: 'u-form',
|
||||
props: {
|
||||
// 当前form的需要验证字段的集合
|
||||
model: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
// 验证规则
|
||||
// rules: {
|
||||
// type: [Object, Function, Array],
|
||||
// default() {
|
||||
// return {};
|
||||
// }
|
||||
// },
|
||||
// 有错误时的提示方式,message-提示信息,border-如果input设置了边框,变成呈红色,
|
||||
// border-bottom-下边框呈现红色,none-无提示
|
||||
errorType: {
|
||||
type: Array,
|
||||
default() {
|
||||
return ['message', 'toast'];
|
||||
},
|
||||
},
|
||||
// 是否显示表单域的下划线边框
|
||||
borderBottom: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// label的位置,left-左边,top-上边
|
||||
labelPosition: {
|
||||
type: String,
|
||||
default: 'left',
|
||||
},
|
||||
// label的宽度,单位rpx
|
||||
labelWidth: {
|
||||
type: [String, Number],
|
||||
default: 90,
|
||||
},
|
||||
// lable字体的对齐方式
|
||||
labelAlign: {
|
||||
type: String,
|
||||
default: 'left',
|
||||
},
|
||||
// lable的样式,对象形式
|
||||
labelStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
uForm: this,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rules: {},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 存储当前form下的所有u-form-item的实例
|
||||
// 不能定义在data中,否则微信小程序会造成循环引用而报错
|
||||
this.fields = [];
|
||||
},
|
||||
methods: {
|
||||
setRules(rules) {
|
||||
this.rules = rules;
|
||||
},
|
||||
// 清空所有u-form-item组件的内容,本质上是调用了u-form-item组件中的resetField()方法
|
||||
resetFields() {
|
||||
this.fields.map((field) => {
|
||||
field.resetField();
|
||||
});
|
||||
},
|
||||
getProperty(obj, key) {
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
if (typeof key !== 'string' || key === '') {
|
||||
return '';
|
||||
}
|
||||
if (key.indexOf('.') !== -1) {
|
||||
const keys = key.split('.');
|
||||
let firstObj = obj[keys[0]] || {};
|
||||
|
||||
for (let i = 1; i < keys.length; i++) {
|
||||
if (firstObj) {
|
||||
firstObj = firstObj[keys[i]];
|
||||
}
|
||||
}
|
||||
return firstObj;
|
||||
}
|
||||
return obj[key];
|
||||
},
|
||||
// 对部分表单字段进行校验
|
||||
async validateField(value, callback, event = null) {
|
||||
// $nextTick是必须的,否则model的变更,可能会延后于此方法的执行
|
||||
this.$nextTick(() => {
|
||||
// 校验错误信息,返回给回调方法,用于存放所有form-item的错误信息
|
||||
const errorsRes = [];
|
||||
// 如果为字符串,转为数组
|
||||
value = [].concat(value);
|
||||
// 历遍fields所有子form-item
|
||||
this.fields.map((child) => {
|
||||
// 用于存放form-item的错误信息
|
||||
const childErrors = [];
|
||||
if (value.includes(child.prop)) {
|
||||
const rule = this.rules[child.prop];
|
||||
if (!rule) return;
|
||||
const rules = [].concat(rule);
|
||||
|
||||
// 对rules数组进行校验
|
||||
for (let i = 0; i < rules.length; i++) {
|
||||
child.validation('', (error) => {
|
||||
if (error) {
|
||||
childErrors.push(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.errorType.indexOf('none') === -1 && this.errorType.indexOf('toast') >= 0 && childErrors.length) {
|
||||
this.$u.toast(childErrors[0]);
|
||||
}
|
||||
typeof callback === 'function' && callback(childErrors);
|
||||
}
|
||||
});
|
||||
// if (this.errorType.indexOf('none') === -1 && this.errorType.indexOf('toast') >= 0 && errorsRes.length) {
|
||||
// this.$u.toast(errorsRes[0].me);
|
||||
// }
|
||||
// // 执行回调函数
|
||||
// typeof callback === 'function' && callback(errorsRes);
|
||||
});
|
||||
},
|
||||
// 校验全部数据
|
||||
validate(callback) {
|
||||
return new Promise((resolve) => {
|
||||
// 对所有的u-form-item进行校验
|
||||
let valid = true; // 默认通过
|
||||
let count = 0; // 用于标记是否检查完毕
|
||||
let errorArr = []; // 存放错误信息
|
||||
this.fields.map((field) => {
|
||||
// 调用每一个u-form-item实例的validation的校验方法
|
||||
field.validation('', (error) => {
|
||||
// 如果任意一个u-form-item校验不通过,就意味着整个表单不通过
|
||||
if (error) {
|
||||
valid = false;
|
||||
errorArr.push(error);
|
||||
}
|
||||
// 当历遍了所有的u-form-item时,调用promise的then方法
|
||||
if (++count === this.fields.length) {
|
||||
resolve(valid); // 进入promise的then方法
|
||||
// // 判断是否设置了toast的提示方式,只提示最前面的表单域的第一个错误信息
|
||||
if (this.errorType.indexOf('none') === -1 && this.errorType.indexOf('toast') >= 0 && errorArr.length) {
|
||||
this.$u.toast(errorArr[0]);
|
||||
}
|
||||
// // 调用回调方法
|
||||
if (typeof callback == 'function') callback(valid);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import 'uview-ui/libs/css/style.components.scss';
|
||||
</style>
|
||||
|
|
@ -0,0 +1,286 @@
|
|||
<template>
|
||||
<view class="u-image" @tap="onClick" :style="[wrapStyle, backgroundStyle]">
|
||||
<image
|
||||
v-if="!isError"
|
||||
:src="src"
|
||||
:mode="mode"
|
||||
@error="onErrorHandler"
|
||||
@load="onLoadHandler"
|
||||
:lazy-load="lazyLoad"
|
||||
class="u-image__image"
|
||||
:show-menu-by-longpress="showMenuByLongpress"
|
||||
:style="{
|
||||
borderRadius: shape == 'circle' ? '50%' :$u.addUnit(borderRadius) ,
|
||||
borderTopRightRadius:borderTopRightRadius?$u.addUnit(borderTopRightRadius):'',
|
||||
borderTopLeftRadius:borderTopLeftRadius?$u.addUnit(borderTopLeftRadius):'',
|
||||
borderBottomLeftRadius:borderBottomLeftRadius?$u.addUnit(borderBottomLeftRadius):'',
|
||||
borderBottomRightRadius:borderBottomRightRadius?$u.addUnit(borderBottomRightRadius):'',
|
||||
}"
|
||||
></image>
|
||||
<view
|
||||
v-if="showLoading && loading"
|
||||
class="u-image__loading"
|
||||
:style="{
|
||||
borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius),
|
||||
backgroundColor: this.bgColor
|
||||
}"
|
||||
>
|
||||
<slot v-if="$slots.loading" name="loading" />
|
||||
<u-icon v-else :name="loadingIcon" :width="width" :height="height"></u-icon>
|
||||
</view>
|
||||
<view
|
||||
v-if="showError && isError && !loading"
|
||||
class="u-image__error"
|
||||
:style="{
|
||||
borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius)
|
||||
}"
|
||||
>
|
||||
<slot v-if="$slots.error" name="error" />
|
||||
<u-icon v-else :name="errorIcon" :width="width" :height="height"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Image 图片
|
||||
* @description 此组件为uni-app的image组件的加强版,在继承了原有功能外,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。
|
||||
* @tutorial https://uviewui.com/components/image.html
|
||||
* @property {String} src 图片地址
|
||||
* @property {String} mode 裁剪模式,见官网说明
|
||||
* @property {String | Number} width 宽度,单位任意,如果为数值,则为rpx单位(默认100%)
|
||||
* @property {String | Number} height 高度,单位任意,如果为数值,则为rpx单位(默认 auto)
|
||||
* @property {String} shape 图片形状,circle-圆形,square-方形(默认square)
|
||||
* @property {String | Number} border-radius 圆角值,单位任意,如果为数值,则为rpx单位(默认 0)
|
||||
* @property {Boolean} lazy-load 是否懒加载,仅微信小程序、App、百度小程序、字节跳动小程序有效(默认 true)
|
||||
* @property {Boolean} show-menu-by-longpress 是否开启长按图片显示识别小程序码菜单,仅微信小程序有效(默认 false)
|
||||
* @property {String} loading-icon 加载中的图标,或者小图片(默认 photo)
|
||||
* @property {String} error-icon 加载失败的图标,或者小图片(默认 error-circle)
|
||||
* @property {Boolean} show-loading 是否显示加载中的图标或者自定义的slot(默认 true)
|
||||
* @property {Boolean} show-error 是否显示加载错误的图标或者自定义的slot(默认 true)
|
||||
* @property {Boolean} fade 是否需要淡入效果(默认 true)
|
||||
* @property {String Number} width 传入图片路径时图片的宽度
|
||||
* @property {String Number} height 传入图片路径时图片的高度
|
||||
* @property {Boolean} webp 只支持网络资源,只对微信小程序有效(默认 false)
|
||||
* @property {String | Number} duration 搭配fade参数的过渡时间,单位ms(默认 500)
|
||||
* @event {Function} click 点击图片时触发
|
||||
* @event {Function} error 图片加载失败时触发
|
||||
* @event {Function} load 图片加载成功时触发
|
||||
* @example <u-image width="100%" height="300rpx" :src="src"></u-image>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-image',
|
||||
props: {
|
||||
// 图片地址
|
||||
src: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 裁剪模式
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'aspectFill'
|
||||
},
|
||||
// 宽度,单位任意
|
||||
width: {
|
||||
type: [String, Number],
|
||||
default: '100%'
|
||||
},
|
||||
// 高度,单位任意
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: 'auto'
|
||||
},
|
||||
// 图片形状,circle-圆形,square-方形
|
||||
shape: {
|
||||
type: String,
|
||||
default: 'square'
|
||||
},
|
||||
// 圆角,单位任意
|
||||
borderRadius: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 是否懒加载,微信小程序、App、百度小程序、字节跳动小程序
|
||||
lazyLoad: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 开启长按图片显示识别微信小程序码菜单
|
||||
showMenuByLongpress: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 加载中的图标,或者小图片
|
||||
loadingIcon: {
|
||||
type: String,
|
||||
default: 'photo'
|
||||
},
|
||||
// 加载失败的图标,或者小图片
|
||||
errorIcon: {
|
||||
type: String,
|
||||
default: 'error-circle'
|
||||
},
|
||||
// 是否显示加载中的图标或者自定义的slot
|
||||
showLoading: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否显示加载错误的图标或者自定义的slot
|
||||
showError: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否需要淡入效果
|
||||
fade: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 只支持网络资源,只对微信小程序有效
|
||||
webp: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 过渡时间,单位ms
|
||||
duration: {
|
||||
type: [String, Number],
|
||||
default: 500
|
||||
},
|
||||
// 背景颜色,用于深色页面加载图片时,为了和背景色融合
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#f3f4f6'
|
||||
},
|
||||
borderTopRightRadius:{
|
||||
type: [String, Number],
|
||||
default:0
|
||||
},
|
||||
borderTopLeftRadius:{
|
||||
type: [String, Number],
|
||||
default:0
|
||||
},
|
||||
borderBottomLeftRadius:{
|
||||
type: [String, Number],
|
||||
default:0
|
||||
},
|
||||
borderBottomRightRadius:{
|
||||
type: [String, Number],
|
||||
default:0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 图片是否加载错误,如果是,则显示错误占位图
|
||||
isError: false,
|
||||
// 初始化组件时,默认为加载中状态
|
||||
loading: true,
|
||||
// 不透明度,为了实现淡入淡出的效果
|
||||
opacity: 1,
|
||||
// 过渡时间,因为props的值无法修改,故需要一个中间值
|
||||
durationTime: this.duration,
|
||||
// 图片加载完成时,去掉背景颜色,因为如果是png图片,就会显示灰色的背景
|
||||
backgroundStyle: {}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
src: {
|
||||
immediate: true,
|
||||
handler (n) {
|
||||
if(!n) {
|
||||
// 如果传入null或者'',或者false,或者undefined,标记为错误状态
|
||||
this.isError = true;
|
||||
this.loading = false;
|
||||
} else {
|
||||
this.isError = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
wrapStyle() {
|
||||
let style = {};
|
||||
// 通过调用addUnit()方法,如果有单位,如百分比,px单位等,直接返回,如果是纯粹的数值,则加上rpx单位
|
||||
style.width = this.$u.addUnit(this.width);
|
||||
style.height = this.$u.addUnit(this.height);
|
||||
// 如果是配置了圆形,设置50%的圆角,否则按照默认的配置值
|
||||
style.borderRadius = this.shape == 'circle' ? '50%' : this.$u.addUnit(this.borderRadius);
|
||||
// 如果设置圆角,必须要有hidden,否则可能圆角无效
|
||||
style.overflow = this.borderRadius > 0 ? 'hidden' : 'visible';
|
||||
if (this.fade) {
|
||||
style.opacity = this.opacity;
|
||||
style.transition = `opacity ${Number(this.durationTime) / 1000}s ease-in-out`;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 点击图片
|
||||
onClick() {
|
||||
this.$emit('click');
|
||||
},
|
||||
// 图片加载失败
|
||||
onErrorHandler(err) {
|
||||
this.loading = false;
|
||||
this.isError = true;
|
||||
this.$emit('error', err);
|
||||
},
|
||||
// 图片加载完成,标记loading结束
|
||||
onLoadHandler() {
|
||||
this.loading = false;
|
||||
this.isError = false;
|
||||
this.$emit('load');
|
||||
// 如果不需要动画效果,就不执行下方代码,同时移除加载时的背景颜色
|
||||
// 否则无需fade效果时,png图片依然能看到下方的背景色
|
||||
if (!this.fade) return this.removeBgColor();
|
||||
// 原来opacity为1(不透明,是为了显示占位图),改成0(透明,意味着该元素显示的是背景颜色,默认的灰色),再改成1,是为了获得过渡效果
|
||||
this.opacity = 0;
|
||||
// 这里设置为0,是为了图片展示到背景全透明这个过程时间为0,延时之后延时之后重新设置为duration,是为了获得背景透明(灰色)
|
||||
// 到图片展示的过程中的淡入效果
|
||||
this.durationTime = 0;
|
||||
// 延时50ms,否则在浏览器H5,过渡效果无效
|
||||
setTimeout(() => {
|
||||
this.durationTime = this.duration;
|
||||
this.opacity = 1;
|
||||
setTimeout(() => {
|
||||
this.removeBgColor();
|
||||
}, this.durationTime);
|
||||
}, 50);
|
||||
},
|
||||
// 移除图片的背景色
|
||||
removeBgColor() {
|
||||
// 淡入动画过渡完成后,将背景设置为透明色,否则png图片会看到灰色的背景
|
||||
this.backgroundStyle = {
|
||||
backgroundColor: 'transparent'
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import 'uview-ui/libs/css/style.components.scss';
|
||||
|
||||
.u-image {
|
||||
position: relative;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
&__image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__loading,
|
||||
&__error {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: $u-bg-color;
|
||||
color: $u-tips-color;
|
||||
font-size: 46rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
<template>
|
||||
<view>
|
||||
<cu-popup :zoom="zoom" mode="center" :popup="false" :z-index="uZIndex" v-model="value" :length="width"
|
||||
:mask-close-able="maskCloseAble" :border-radius="borderRadius" @close="popupClose" :negative-top="negativeTop">
|
||||
<view class="u-model">
|
||||
<view v-if="showTitle" class="u-model__title u-line-1" :style="[titleStyle]">{{ title }}</view>
|
||||
<view class="u-model__content">
|
||||
<view :style="[contentStyle]" v-if="$slots.default || $slots.$default">
|
||||
<slot />
|
||||
</view>
|
||||
<view v-else class="u-model__content__message" :style="[contentStyle]">{{ content }}</view>
|
||||
</view>
|
||||
<view class="u-model__footer u-border-top" v-if="showCancelButton || showConfirmButton">
|
||||
<view v-if="showCancelButton" :hover-stay-time="100" hover-class="u-model__btn--hover" class="u-model__footer__button"
|
||||
:style="[cancelBtnStyle]" @tap="cancel">
|
||||
{{cancelText}}
|
||||
</view>
|
||||
<view v-if="showConfirmButton || $slots['confirm-button']" :hover-stay-time="100" :hover-class="asyncClose ? 'none' : 'u-model__btn--hover'"
|
||||
class="u-model__footer__button hairline-left" :style="[confirmBtnStyle]" @tap="confirm">
|
||||
<slot v-if="$slots['confirm-button']" name="confirm-button"></slot>
|
||||
<block v-else>
|
||||
<u-loading mode="circle" :color="confirmColor" v-if="loading && showLoading"></u-loading>
|
||||
<block v-else>
|
||||
{{confirmText}}
|
||||
</block>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</cu-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* modal 模态框
|
||||
* @description 弹出模态框,常用于消息提示、消息确认、在当前页面内完成特定的交互操作
|
||||
* @tutorial https://www.uviewui.com/components/modal.html
|
||||
* @property {Boolean} value 是否显示模态框
|
||||
* @property {String | Number} z-index 层级
|
||||
* @property {String} title 模态框标题(默认"提示")
|
||||
* @property {String | Number} width 模态框宽度(默认600)
|
||||
* @property {String} content 模态框内容(默认"内容")
|
||||
* @property {Boolean} show-title 是否显示标题(默认true)
|
||||
* @property {Boolean} async-close 是否异步关闭,只对确定按钮有效(默认false)
|
||||
* @property {Boolean} show-confirm-button 是否显示确认按钮(默认true)
|
||||
* @property {Stringr | Number} negative-top modal往上偏移的值
|
||||
* @property {Boolean} show-cancel-button 是否显示取消按钮(默认false)
|
||||
* @property {Boolean} mask-close-able 是否允许点击遮罩关闭modal(默认false)
|
||||
* @property {String} confirm-text 确认按钮的文字内容(默认"确认")
|
||||
* @property {String} cancel-text 取消按钮的文字内容(默认"取消")
|
||||
* @property {String} cancel-color 取消按钮的颜色(默认"#606266")
|
||||
* @property {String} confirm-color 确认按钮的文字内容(默认"#2979ff")
|
||||
* @property {String | Number} border-radius 模态框圆角值,单位rpx(默认16)
|
||||
* @property {Object} title-style 自定义标题样式,对象形式
|
||||
* @property {Object} content-style 自定义内容样式,对象形式
|
||||
* @property {Object} cancel-style 自定义取消按钮样式,对象形式
|
||||
* @property {Object} confirm-style 自定义确认按钮样式,对象形式
|
||||
* @property {Boolean} zoom 是否开启缩放模式(默认true)
|
||||
* @event {Function} confirm 确认按钮被点击
|
||||
* @event {Function} cancel 取消按钮被点击
|
||||
* @example <cu-modal :src="title" :content="content"></cu-modal>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-modal',
|
||||
props: {
|
||||
// loading
|
||||
showLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否显示Modal
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 层级z-index
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
// 标题
|
||||
title: {
|
||||
type: [String],
|
||||
default: '提示'
|
||||
},
|
||||
// 弹窗宽度,可以是数值(rpx),百分比,auto等
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: 600
|
||||
},
|
||||
// 弹窗内容
|
||||
content: {
|
||||
type: String,
|
||||
default: '内容'
|
||||
},
|
||||
// 是否显示标题
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否显示确认按钮
|
||||
showConfirmButton: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否显示取消按钮
|
||||
showCancelButton: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 确认文案
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: '确认'
|
||||
},
|
||||
// 取消文案
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: '取消'
|
||||
},
|
||||
// 确认按钮颜色
|
||||
confirmColor: {
|
||||
type: String,
|
||||
default: '#378264'
|
||||
},
|
||||
// 取消文字颜色
|
||||
cancelColor: {
|
||||
type: String,
|
||||
default: '#606266'
|
||||
},
|
||||
// 圆角值
|
||||
borderRadius: {
|
||||
type: [Number, String],
|
||||
default: 16
|
||||
},
|
||||
// 标题的样式
|
||||
titleStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 内容的样式
|
||||
contentStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 取消按钮的样式
|
||||
cancelStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 确定按钮的样式
|
||||
confirmStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 是否开启缩放效果
|
||||
zoom: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否异步关闭,只对确定按钮有效
|
||||
asyncClose: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否允许点击遮罩关闭modal
|
||||
maskCloseAble: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 给一个负的margin-top,往上偏移,避免和键盘重合的情况
|
||||
negativeTop: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false, // 确认按钮是否正在加载中
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cancelBtnStyle() {
|
||||
return Object.assign({
|
||||
color: this.cancelColor
|
||||
}, this.cancelStyle);
|
||||
},
|
||||
confirmBtnStyle() {
|
||||
return Object.assign({
|
||||
color: this.confirmColor
|
||||
}, this.confirmStyle);
|
||||
},
|
||||
uZIndex() {
|
||||
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 如果是异步关闭时,外部修改v-model的值为false时,重置内部的loading状态
|
||||
// 避免下次打开的时候,状态混乱
|
||||
value(n) {
|
||||
if (n === true) this.loading = false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
// 异步关闭
|
||||
if (this.asyncClose) {
|
||||
this.loading = true;
|
||||
} else {
|
||||
this.$emit('input', false);
|
||||
}
|
||||
this.$emit('confirm');
|
||||
},
|
||||
cancel() {
|
||||
this.$emit('cancel');
|
||||
this.$emit('input', false);
|
||||
// 目前popup弹窗关闭有一个延时操作,此处做一个延时
|
||||
// 避免确认按钮文字变成了"确定"字样,modal还没消失,造成视觉不好的效果
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
}, 300);
|
||||
},
|
||||
// 点击遮罩关闭modal,设置v-model的值为false,否则无法第二次弹起modal
|
||||
popupClose() {
|
||||
this.$emit('input', false);
|
||||
},
|
||||
// 清除加载中的状态
|
||||
clearLoading() {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "uView-ui/libs/css/style.components.scss";
|
||||
|
||||
.u-model {
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
font-size: 32rpx;
|
||||
background-color: #fff;
|
||||
|
||||
&__btn--hover {
|
||||
background-color: rgb(230, 230, 230);
|
||||
}
|
||||
|
||||
&__title {
|
||||
padding-top: 48rpx;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
color: $u-main-color;
|
||||
}
|
||||
|
||||
&__content {
|
||||
&__message {
|
||||
padding: 48rpx;
|
||||
font-size: 30rpx;
|
||||
text-align: center;
|
||||
color: $u-content-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
@include vue-flex;
|
||||
|
||||
&__button {
|
||||
flex: 1;
|
||||
height: 100rpx;
|
||||
line-height: 100rpx;
|
||||
font-size: 32rpx;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,323 @@
|
|||
<template>
|
||||
<view class="">
|
||||
<view class="u-navbar" :style="[navbarStyle]"
|
||||
:class="{ 'u-navbar-fixed': isFixed, 'u-border-bottom': borderBottom }">
|
||||
<view class="u-status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||||
<view class="u-navbar-inner" :style="[navbarInnerStyle]">
|
||||
|
||||
<view class="u-back-wrap" v-if="isBack && !$slots.left" @tap="goBack">
|
||||
<view class="u-icon-wrap">
|
||||
<u-icon :name="backIconName" :color="backIconColor" :size="backIconSize"></u-icon>
|
||||
</view>
|
||||
<view class="u-icon-wrap u-back-text u-line-1" v-if="backText" :style="[backTextStyle]">
|
||||
{{ backText }}</view>
|
||||
</view>
|
||||
<view class="u-slot-left" v-if="$slots.left">
|
||||
<slot name="left"></slot>
|
||||
</view>
|
||||
<view class="u-navbar-content-title" v-if="title" :style="[titleStyle]">
|
||||
<view class="u-title u-line-1" :style="{
|
||||
color: titleColor,
|
||||
fontSize: titleSize + 'rpx',
|
||||
fontWeight: titleBold ? 'bold' : 'normal'
|
||||
}">
|
||||
{{ title }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-slot-content">
|
||||
<slot></slot>
|
||||
</view>
|
||||
<view class="u-slot-right">
|
||||
<slot name="right"></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 解决fixed定位后导航栏塌陷的问题 -->
|
||||
<view class="u-navbar-placeholder" v-if="isFixed && !immersive"
|
||||
:style="{ width: '100%', height: Number(navbarHeight) + statusBarHeight + 'px' }"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Emitter from 'uview-ui/libs/util/emitter.js';
|
||||
// 获取系统状态栏的高度
|
||||
let systemInfo = uni.getSystemInfoSync();
|
||||
let menuButtonInfo = {};
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
/**
|
||||
* navbar 自定义导航栏
|
||||
* @description 此组件一般用于在特殊情况下,需要自定义导航栏的时候用到,一般建议使用uniapp自带的导航栏。
|
||||
* @tutorial https://www.uviewui.com/components/navbar.html
|
||||
* @property {String Number} height 导航栏高度(不包括状态栏高度在内,内部自动加上),注意这里的单位是px(默认44)
|
||||
* @property {String} back-icon-color 左边返回图标的颜色(默认#606266)
|
||||
* @property {String} back-icon-name 左边返回图标的名称,只能为uView自带的图标(默认arrow-left)
|
||||
* @property {String Number} back-icon-size 左边返回图标的大小,单位rpx(默认30)
|
||||
* @property {String} back-text 返回图标右边的辅助提示文字
|
||||
* @property {Object} back-text-style 返回图标右边的辅助提示文字的样式,对象形式(默认{ color: '#606266' })
|
||||
* @property {String} title 导航栏标题,如设置为空字符,将会隐藏标题占位区域
|
||||
* @property {String Number} title-width 导航栏标题的最大宽度,内容超出会以省略号隐藏,单位rpx(默认250)
|
||||
* @property {String} title-color 标题的颜色(默认#606266)
|
||||
* @property {String Number} title-size 导航栏标题字体大小,单位rpx(默认32)
|
||||
* @property {Function} custom-back 自定义返回逻辑方法
|
||||
* @property {String Number} z-index 固定在顶部时的z-index值(默认980)
|
||||
* @property {Boolean} is-back 是否显示导航栏左边返回图标和辅助文字(默认true)
|
||||
* @property {Object} background 导航栏背景设置,见官网说明(默认{ background: '#ffffff' })
|
||||
* @property {Boolean} is-fixed 导航栏是否固定在顶部(默认true)
|
||||
* @property {Boolean} immersive 沉浸式,允许fixed定位后导航栏塌陷,仅fixed定位下生效(默认false)
|
||||
* @property {Boolean} border-bottom 导航栏底部是否显示下边框,如定义了较深的背景颜色,可取消此值(默认true)
|
||||
* @example <u-navbar back-text="返回" title="剑未配妥,出门已是江湖"></u-navbar>
|
||||
*/
|
||||
export default {
|
||||
name: "cu-navbar",
|
||||
mixins: [Emitter],
|
||||
props: {
|
||||
// 导航栏高度,单位px,非rpx
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
// 返回箭头的颜色
|
||||
backIconColor: {
|
||||
type: String,
|
||||
default: '#606266'
|
||||
},
|
||||
// 左边返回的图标
|
||||
backIconName: {
|
||||
type: String,
|
||||
default: 'nav-back'
|
||||
},
|
||||
// 左边返回图标的大小,rpx
|
||||
backIconSize: {
|
||||
type: [String, Number],
|
||||
default: '44'
|
||||
},
|
||||
// 返回的文字提示
|
||||
backText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 返回的文字的 样式
|
||||
backTextStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
color: '#606266'
|
||||
}
|
||||
}
|
||||
},
|
||||
// 导航栏标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 标题的宽度,如果需要自定义右侧内容,且右侧内容很多时,可能需要减少这个宽度,单位rpx
|
||||
titleWidth: {
|
||||
type: [String, Number],
|
||||
default: '250'
|
||||
},
|
||||
// 标题的颜色
|
||||
titleColor: {
|
||||
type: String,
|
||||
default: '#606266'
|
||||
},
|
||||
// 标题字体是否加粗
|
||||
titleBold: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 标题的字体大小
|
||||
titleSize: {
|
||||
type: [String, Number],
|
||||
default: 32
|
||||
},
|
||||
isBack: {
|
||||
type: [Boolean, String],
|
||||
default: true
|
||||
},
|
||||
// 对象形式,因为用户可能定义一个纯色,或者线性渐变的颜色
|
||||
background: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
background: '#ffffff'
|
||||
}
|
||||
}
|
||||
},
|
||||
// 导航栏是否固定在顶部
|
||||
isFixed: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否沉浸式,允许fixed定位后导航栏塌陷,仅fixed定位下生效
|
||||
immersive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否显示导航栏的下边框
|
||||
borderBottom: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
zIndex: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
// 自定义返回逻辑
|
||||
customBack: {
|
||||
type: Function,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
menuButtonInfo: menuButtonInfo,
|
||||
statusBarHeight: systemInfo.statusBarHeight
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 导航栏内部盒子的样式
|
||||
navbarInnerStyle() {
|
||||
let style = {};
|
||||
// 导航栏宽度,如果在小程序下,导航栏宽度为胶囊的左边到屏幕左边的距离
|
||||
style.height = this.navbarHeight + 'px';
|
||||
// // 如果是各家小程序,导航栏内部的宽度需要减少右边胶囊的宽度
|
||||
// #ifdef MP
|
||||
let rightButtonWidth = systemInfo.windowWidth - menuButtonInfo.left;
|
||||
style.marginRight = rightButtonWidth + 'px';
|
||||
// #endif
|
||||
return style;
|
||||
},
|
||||
// 整个导航栏的样式
|
||||
navbarStyle() {
|
||||
let style = {};
|
||||
style.zIndex = this.zIndex ? this.zIndex : this.$u.zIndex.navbar;
|
||||
// 合并用户传递的背景色对象
|
||||
Object.assign(style, this.background);
|
||||
return style;
|
||||
},
|
||||
// 导航中间的标题的样式
|
||||
titleStyle() {
|
||||
let style = {};
|
||||
// #ifndef MP
|
||||
style.left = (systemInfo.windowWidth - uni.upx2px(this.titleWidth)) / 2 + 'px';
|
||||
style.right = (systemInfo.windowWidth - uni.upx2px(this.titleWidth)) / 2 + 'px';
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
// 此处是为了让标题显示区域即使在小程序有右侧胶囊的情况下也能处于屏幕的中间,是通过绝对定位实现的
|
||||
let rightButtonWidth = systemInfo.windowWidth - menuButtonInfo.left;
|
||||
style.left = (systemInfo.windowWidth - uni.upx2px(this.titleWidth)) / 2 + 'px';
|
||||
style.right = rightButtonWidth - (systemInfo.windowWidth - uni.upx2px(this.titleWidth)) / 2 +
|
||||
rightButtonWidth +
|
||||
'px';
|
||||
// #endif
|
||||
style.width = uni.upx2px(this.titleWidth) + 'px';
|
||||
return style;
|
||||
},
|
||||
// 转换字符数值为真正的数值
|
||||
navbarHeight() {
|
||||
// #ifdef APP-PLUS || H5
|
||||
return this.height ? this.height : 44;
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
// 小程序特别处理,让导航栏高度 = 胶囊高度 + 两倍胶囊顶部与状态栏底部的距离之差(相当于同时获得了导航栏底部与胶囊底部的距离)
|
||||
// 此方法有缺陷,暂不用(会导致少了几个px),采用直接固定值的方式
|
||||
// return menuButtonInfo.height + (menuButtonInfo.top - this.statusBarHeight) * 2;//导航高度
|
||||
let height = systemInfo.platform == 'ios' ? 44 : 48;
|
||||
return this.height ? this.height : height;
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
methods: {
|
||||
goBack() {
|
||||
// 如果自定义了点击返回按钮的函数,则执行,否则执行返回逻辑
|
||||
if (typeof this.customBack === 'function') {
|
||||
// 在微信,支付宝等环境(H5正常),会导致父组件定义的customBack()函数体中的this变成子组件的this
|
||||
// 通过bind()方法,绑定父组件的this,让this.customBack()的this为父组件的上下文
|
||||
this.customBack.bind(this.$u.$parent.call(this))();
|
||||
} else {
|
||||
uni.navigateBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "uview-ui/libs/css/style.components.scss";
|
||||
|
||||
.u-navbar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.u-navbar-fixed {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 991;
|
||||
}
|
||||
|
||||
.u-status-bar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.u-navbar-inner {
|
||||
@include vue-flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.u-back-wrap {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
flex-grow: 0;
|
||||
padding: 14rpx 14rpx 14rpx 24rpx;
|
||||
}
|
||||
|
||||
.u-back-text {
|
||||
padding-left: 4rpx;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.u-navbar-content-title {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60rpx;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.u-navbar-centent-slot {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.u-title {
|
||||
line-height: 60rpx;
|
||||
font-size: 32rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.u-navbar-right {
|
||||
flex: 1;
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.u-slot-content {
|
||||
flex: 1;
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,456 @@
|
|||
<template>
|
||||
<view v-if="visibleSync" :style="[customStyle, {
|
||||
zIndex: uZindex - 1
|
||||
}]" class="u-drawer" hover-stop-propagation>
|
||||
<u-mask :duration="duration" :custom-style="maskCustomStyle" :maskClickAble="maskCloseAble" :z-index="uZindex - 2" :show="showDrawer && mask" @click="maskClick"></u-mask>
|
||||
<view
|
||||
class="u-drawer-content"
|
||||
@tap="modeCenterClose(mode)"
|
||||
:class="[
|
||||
safeAreaInsetBottom ? 'safe-area-inset-bottom' : '',
|
||||
'u-drawer-' + mode,
|
||||
showDrawer ? 'u-drawer-content-visible' : '',
|
||||
zoom && mode == 'center' ? 'u-animation-zoom' : ''
|
||||
]"
|
||||
@touchmove.stop.prevent="() => {}"
|
||||
@tap.stop.prevent="() => {}"
|
||||
:style="[style]"
|
||||
>
|
||||
<view class="u-mode-center-box" @tap.stop.prevent="() => {}" @touchmove.stop.prevent="() => {}" v-if="mode == 'center'" :style="[centerStyle]">
|
||||
<u-icon
|
||||
@click="close"
|
||||
v-if="closeable"
|
||||
class="u-close"
|
||||
:class="['u-close--' + closeIconPos]"
|
||||
:name="closeIcon"
|
||||
:color="closeIconColor"
|
||||
:size="closeIconSize"
|
||||
></u-icon>
|
||||
<scroll-view class="u-drawer__scroll-view" scroll-y="true">
|
||||
<slot />
|
||||
</scroll-view>
|
||||
</view>
|
||||
<scroll-view class="u-drawer__scroll-view" scroll-y="true" v-else>
|
||||
<slot />
|
||||
</scroll-view>
|
||||
<view @tap="close" class="u-close" :class="['u-close--' + closeIconPos]">
|
||||
<u-icon
|
||||
v-if="mode != 'center' && closeable"
|
||||
:name="closeIcon"
|
||||
:color="closeIconColor"
|
||||
:size="closeIconSize"
|
||||
></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* popup 弹窗
|
||||
* @description 弹出层容器,用于展示弹窗、信息提示等内容,支持上、下、左、右和中部弹出。组件只提供容器,内部内容由用户自定义
|
||||
* @tutorial https://www.uviewui.com/components/popup.html
|
||||
* @property {String} mode 弹出方向(默认left)
|
||||
* @property {Boolean} mask 是否显示遮罩(默认true)
|
||||
* @property {Stringr | Number} length mode=left | 见官网说明(默认auto)
|
||||
* @property {Boolean} zoom 是否开启缩放动画,只在mode为center时有效(默认true)
|
||||
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
|
||||
* @property {Boolean} mask-close-able 点击遮罩是否可以关闭弹出层(默认true)
|
||||
* @property {Object} custom-style 用户自定义样式
|
||||
* @property {Stringr | Number} negative-top 中部弹出时,往上偏移的值
|
||||
* @property {Numberr | String} border-radius 弹窗圆角值(默认0)
|
||||
* @property {Numberr | String} z-index 弹出内容的z-index值(默认1075)
|
||||
* @property {Boolean} closeable 是否显示关闭图标(默认false)
|
||||
* @property {String} close-icon 关闭图标的名称,只能uView的内置图标
|
||||
* @property {String} close-icon-pos 自定义关闭图标位置(默认top-right)
|
||||
* @property {String} close-icon-color 关闭图标的颜色(默认#909399)
|
||||
* @property {Number | String} close-icon-size 关闭图标的大小,单位rpx(默认30)
|
||||
* @event {Function} open 弹出层打开
|
||||
* @event {Function} close 弹出层收起
|
||||
* @example <cu-popup v-model="show"><view>出淤泥而不染,濯清涟而不妖</view></cu-popup>
|
||||
*/
|
||||
export default {
|
||||
name: 'cu-popup',
|
||||
props: {
|
||||
/**
|
||||
* 显示状态
|
||||
*/
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* 弹出方向,left|right|top|bottom|center
|
||||
*/
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'left'
|
||||
},
|
||||
/**
|
||||
* 是否显示遮罩
|
||||
*/
|
||||
mask: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 抽屉的宽度(mode=left|right),或者高度(mode=top|bottom),单位rpx,或者"auto"
|
||||
// 或者百分比"50%",表示由内容撑开高度或者宽度
|
||||
length: {
|
||||
type: [Number, String],
|
||||
default: 'auto'
|
||||
},
|
||||
// 是否开启缩放动画,只在mode=center时有效
|
||||
zoom: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距
|
||||
safeAreaInsetBottom: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否可以通过点击遮罩进行关闭
|
||||
maskCloseAble: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 用户自定义样式
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 此为内部参数,不在文档对外使用,为了解决Picker和keyboard等融合了弹窗的组件
|
||||
// 对v-model双向绑定多层调用造成报错不能修改props值的问题
|
||||
popup: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 显示显示弹窗的圆角,单位rpx
|
||||
borderRadius: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
// 是否显示关闭图标
|
||||
closeable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 关闭图标的名称,只能uView的内置图标
|
||||
closeIcon: {
|
||||
type: String,
|
||||
default: 'close'
|
||||
},
|
||||
// 自定义关闭图标位置,top-left为左上角,top-right为右上角,bottom-left为左下角,bottom-right为右下角
|
||||
closeIconPos: {
|
||||
type: String,
|
||||
default: 'top-right'
|
||||
},
|
||||
// 关闭图标的颜色
|
||||
closeIconColor: {
|
||||
type: String,
|
||||
default: '#909399'
|
||||
},
|
||||
// 关闭图标的大小,单位rpx
|
||||
closeIconSize: {
|
||||
type: [String, Number],
|
||||
default: '30'
|
||||
},
|
||||
// 宽度,只对左,右,中部弹出时起作用,单位rpx,或者"auto"
|
||||
// 或者百分比"50%",表示由内容撑开高度或者宽度,优先级高于length参数
|
||||
width: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 高度,只对上,下,中部弹出时起作用,单位rpx,或者"auto"
|
||||
// 或者百分比"50%",表示由内容撑开高度或者宽度,优先级高于length参数
|
||||
height: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 给一个负的margin-top,往上偏移,避免和键盘重合的情况,仅在mode=center时有效
|
||||
negativeTop: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 遮罩的样式,一般用于修改遮罩的透明度
|
||||
maskCustomStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 遮罩打开或收起的动画过渡时间,单位ms
|
||||
duration: {
|
||||
type: [String, Number],
|
||||
default: 250
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visibleSync: false,
|
||||
showDrawer: false,
|
||||
timer: null,
|
||||
closeFromInner: false, // value的值改变,是发生在内部还是外部
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 根据mode的位置,设定其弹窗的宽度(mode = left|right),或者高度(mode = top|bottom)
|
||||
style() {
|
||||
let style = {};
|
||||
// 如果是左边或者上边弹出时,需要给translate设置为负值,用于隐藏
|
||||
if (this.mode == 'left' || this.mode == 'right') {
|
||||
style = {
|
||||
width: this.width ? this.getUnitValue(this.width) : this.getUnitValue(this.length),
|
||||
height: '100%',
|
||||
transform: `translate3D(${this.mode == 'left' ? '-100%' : '100%'},0px,0px)`
|
||||
};
|
||||
} else if (this.mode == 'top' || this.mode == 'bottom') {
|
||||
style = {
|
||||
width: '100%',
|
||||
height: this.height ? this.getUnitValue(this.height) : this.getUnitValue(this.length),
|
||||
transform: `translate3D(0px,${this.mode == 'top' ? '-100%' : '100%'},0px)`
|
||||
};
|
||||
}
|
||||
style.zIndex = this.uZindex;
|
||||
// 如果用户设置了borderRadius值,添加弹窗的圆角
|
||||
if (this.borderRadius) {
|
||||
switch (this.mode) {
|
||||
case 'left':
|
||||
style.borderRadius = `0 ${this.borderRadius}rpx ${this.borderRadius}rpx 0`;
|
||||
break;
|
||||
case 'top':
|
||||
style.borderRadius = `0 0 ${this.borderRadius}rpx ${this.borderRadius}rpx`;
|
||||
break;
|
||||
case 'right':
|
||||
style.borderRadius = `${this.borderRadius}rpx 0 0 ${this.borderRadius}rpx`;
|
||||
break;
|
||||
case 'bottom':
|
||||
style.borderRadius = `${this.borderRadius}rpx ${this.borderRadius}rpx 0 0`;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
// 不加可能圆角无效
|
||||
style.overflow = 'hidden';
|
||||
}
|
||||
if(this.duration) style.transition = `all ${this.duration / 1000}s linear`;
|
||||
return style;
|
||||
},
|
||||
// 中部弹窗的特有样式
|
||||
centerStyle() {
|
||||
let style = {};
|
||||
style.width = this.width ? this.getUnitValue(this.width) : this.getUnitValue(this.length);
|
||||
// 中部弹出的模式,如果没有设置高度,就用auto值,由内容撑开高度
|
||||
style.height = this.height ? this.getUnitValue(this.height) : 'auto';
|
||||
style.zIndex = this.uZindex;
|
||||
style.marginTop = `-${this.$u.addUnit(this.negativeTop)}`;
|
||||
if (this.borderRadius) {
|
||||
style.borderRadius = `${this.borderRadius}rpx`;
|
||||
// 不加可能圆角无效
|
||||
style.overflow = 'hidden';
|
||||
}
|
||||
return style;
|
||||
},
|
||||
// 计算整理后的z-index值
|
||||
uZindex() {
|
||||
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
if (val) {
|
||||
this.open();
|
||||
} else if(!this.closeFromInner) {
|
||||
this.close();
|
||||
}
|
||||
this.closeFromInner = false;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 组件渲染完成时,检查value是否为true,如果是,弹出popup
|
||||
this.value && this.open();
|
||||
},
|
||||
methods: {
|
||||
// 判断传入的值,是否带有单位,如果没有,就默认用rpx单位
|
||||
getUnitValue(val) {
|
||||
if(/(%|px|rpx|auto)$/.test(val)) return val;
|
||||
else return val + 'rpx'
|
||||
},
|
||||
// 遮罩被点击
|
||||
maskClick() {
|
||||
this.close();
|
||||
},
|
||||
close() {
|
||||
// 标记关闭是内部发生的,否则修改了value值,导致watch中对value检测,导致再执行一遍close
|
||||
// 造成@close事件触发两次
|
||||
this.closeFromInner = true;
|
||||
this.change('showDrawer', 'visibleSync', false);
|
||||
},
|
||||
// 中部弹出时,需要.u-drawer-content将居中内容,此元素会铺满屏幕,点击需要关闭弹窗
|
||||
// 让其只在mode=center时起作用
|
||||
modeCenterClose(mode) {
|
||||
if (mode != 'center' || !this.maskCloseAble) return;
|
||||
this.close();
|
||||
},
|
||||
open() {
|
||||
this.change('visibleSync', 'showDrawer', true);
|
||||
},
|
||||
// 此处的原理是,关闭时先通过动画隐藏弹窗和遮罩,再移除整个组件
|
||||
// 打开时,先渲染组件,延时一定时间再让遮罩和弹窗的动画起作用
|
||||
change(param1, param2, status) {
|
||||
// 如果this.popup为false,意味着为picker,actionsheet等组件调用了popup组件
|
||||
if (this.popup == true) {
|
||||
this.$emit('input', status);
|
||||
}
|
||||
this[param1] = status;
|
||||
if(status) {
|
||||
// #ifdef H5 || MP
|
||||
this.timer = setTimeout(() => {
|
||||
this[param2] = status;
|
||||
this.$emit(status ? 'open' : 'close');
|
||||
}, 50);
|
||||
// #endif
|
||||
// #ifndef H5 || MP
|
||||
this.$nextTick(() => {
|
||||
this[param2] = status;
|
||||
this.$emit(status ? 'open' : 'close');
|
||||
})
|
||||
// #endif
|
||||
} else {
|
||||
this.timer = setTimeout(() => {
|
||||
this[param2] = status;
|
||||
this.$emit(status ? 'open' : 'close');
|
||||
}, this.duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "uview-ui/libs/css/style.components.scss";
|
||||
|
||||
.u-drawer {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.u-drawer-content {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
position: absolute;
|
||||
z-index: 1003;
|
||||
transition: all 0.25s linear;
|
||||
}
|
||||
|
||||
.u-drawer__scroll-view {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.u-drawer-left {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.u-drawer-right {
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.u-drawer-top {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.u-drawer-bottom {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.u-drawer-center {
|
||||
@include vue-flex;
|
||||
flex-direction: column;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.u-mode-center-box {
|
||||
min-width: 100rpx;
|
||||
min-height: 100rpx;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.u-drawer-content-visible.u-drawer-center {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.u-animation-zoom {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.u-drawer-content-visible {
|
||||
transform: translate3D(0px, 0px, 0px) !important;
|
||||
}
|
||||
|
||||
.u-close {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.u-close--top-left {
|
||||
top: 30rpx;
|
||||
left: 30rpx;
|
||||
}
|
||||
|
||||
.u-close--top-right {
|
||||
top: 30rpx;
|
||||
right: 30rpx;
|
||||
}
|
||||
|
||||
.u-close--bottom-left {
|
||||
bottom: 30rpx;
|
||||
left: 30rpx;
|
||||
}
|
||||
|
||||
.u-close--bottom-right {
|
||||
right: 30rpx;
|
||||
bottom: 30rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,417 @@
|
|||
<template>
|
||||
<view class="u-select">
|
||||
<!-- <view class="u-select__action" :class="{
|
||||
'u-select--border': border
|
||||
}" @tap.stop="selectHandler">
|
||||
<view class="u-select__action__icon" :class="{
|
||||
'u-select__action__icon--reverse': value == true
|
||||
}">
|
||||
<u-icon name="arrow-down-fill" size="26" color="#c0c4cc"></u-icon>
|
||||
</view>
|
||||
</view> -->
|
||||
<cu-popup :maskCloseAble="maskCloseAble" mode="bottom" :popup="false" v-model="value" length="auto" :safeAreaInsetBottom="safeAreaInsetBottom" @close="close" :z-index="uZIndex">
|
||||
<view class="u-select">
|
||||
<view class="u-select__header" @touchmove.stop.prevent="">
|
||||
<view
|
||||
class="u-select__header__cancel u-select__header__btn"
|
||||
:style="{ color: cancelColor }"
|
||||
hover-class="u-hover-class"
|
||||
:hover-stay-time="150"
|
||||
@tap="getResult('cancel')"
|
||||
>
|
||||
{{cancelText}}
|
||||
</view>
|
||||
<view class="u-select__header__title">
|
||||
{{title}}
|
||||
</view>
|
||||
<view
|
||||
class="u-select__header__confirm u-select__header__btn"
|
||||
:style="{ color: moving ? cancelColor : confirmColor }"
|
||||
hover-class="u-hover-class"
|
||||
:hover-stay-time="150"
|
||||
@touchmove.stop=""
|
||||
@tap.stop="getResult('confirm')"
|
||||
>
|
||||
{{confirmText}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-select__body">
|
||||
<picker-view @change="columnChange" class="u-select__body__picker-view" :value="defaultSelector" @pickstart="pickstart" @pickend="pickend">
|
||||
<picker-view-column v-for="(item, index) in columnData" :key="index">
|
||||
<view class="u-select__body__picker-view__item" v-for="(item1, index1) in item" :key="index1">
|
||||
<view class="u-line-1">{{ item1[labelName] }}</view>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
</view>
|
||||
</view>
|
||||
</cu-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* select 列选择器
|
||||
* @description 此选择器用于单列,多列,多列联动的选择场景。(从1.3.0版本起,不建议使用Picker组件的单列和多列模式,Select组件是专门为列选择而构造的组件,更简单易用。)
|
||||
* @tutorial http://uviewui.com/components/select.html
|
||||
* @property {String} mode 模式选择,"single-column"-单列模式,"mutil-column"-多列模式,"mutil-column-auto"-多列联动模式
|
||||
* @property {Array} list 列数据,数组形式,见官网说明
|
||||
* @property {Boolean} v-model 布尔值变量,用于控制选择器的弹出与收起
|
||||
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
|
||||
* @property {String} cancel-color 取消按钮的颜色(默认#606266)
|
||||
* @property {String} confirm-color 确认按钮的颜色(默认#2979ff)
|
||||
* @property {String} confirm-text 确认按钮的文字
|
||||
* @property {String} cancel-text 取消按钮的文字
|
||||
* @property {String} default-value 提供的默认选中的下标,见官网说明
|
||||
* @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭Picker(默认true)
|
||||
* @property {String Number} z-index 弹出时的z-index值(默认10075)
|
||||
* @property {String} value-name 自定义list数据的value属性名 1.3.6
|
||||
* @property {String} label-name 自定义list数据的label属性名 1.3.6
|
||||
* @property {String} child-name 自定义list数据的children属性名,只对多列联动模式有效 1.3.7
|
||||
* @event {Function} confirm 点击确定按钮,返回当前选择的值
|
||||
* @example <u-select v-model="show" :list="list"></u-select>
|
||||
*/
|
||||
|
||||
export default {
|
||||
props: {
|
||||
// 列数据
|
||||
list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 是否显示边框
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 通过双向绑定控制组件的弹出与收起
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// "取消"按钮的颜色
|
||||
cancelColor: {
|
||||
type: String,
|
||||
default: '#606266'
|
||||
},
|
||||
// "确定"按钮的颜色
|
||||
confirmColor: {
|
||||
type: String,
|
||||
default: '#2979ff'
|
||||
},
|
||||
// 弹出的z-index值
|
||||
zIndex: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
safeAreaInsetBottom: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否允许通过点击遮罩关闭Picker
|
||||
maskCloseAble: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 提供的默认选中的下标
|
||||
defaultValue: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [0];
|
||||
}
|
||||
},
|
||||
// 模式选择,single-column-单列,mutil-column-多列,mutil-column-auto-多列联动
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'single-column'
|
||||
},
|
||||
// 自定义value属性名
|
||||
valueName: {
|
||||
type: String,
|
||||
default: 'value'
|
||||
},
|
||||
// 自定义label属性名
|
||||
labelName: {
|
||||
type: String,
|
||||
default: 'label'
|
||||
},
|
||||
// 自定义多列联动模式的children属性名
|
||||
childName: {
|
||||
type: String,
|
||||
default: 'children'
|
||||
},
|
||||
// 顶部标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 取消按钮的文字
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: '取消'
|
||||
},
|
||||
// 确认按钮的文字
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: '确认'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 用于列改变时,保存当前的索引,下一次变化时比较得出是哪一列发生了变化
|
||||
defaultSelector: [0],
|
||||
// picker-view的数据
|
||||
columnData: [],
|
||||
// 每次队列发生变化时,保存选择的结果
|
||||
selectValue: [],
|
||||
// 上一次列变化时的index
|
||||
lastSelectIndex: [],
|
||||
// 列数
|
||||
columnNum: 0,
|
||||
// 列是否还在滑动中,微信小程序如果在滑动中就点确定,结果可能不准确
|
||||
moving: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 在select弹起的时候,重新初始化所有数据
|
||||
value: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if(val) setTimeout(() => this.init(), 10);
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
uZIndex() {
|
||||
// 如果用户有传递z-index值,优先使用
|
||||
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 标识滑动开始,只有微信小程序才有这样的事件
|
||||
pickstart() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.moving = true;
|
||||
// #endif
|
||||
},
|
||||
// 标识滑动结束
|
||||
pickend() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.moving = false;
|
||||
// #endif
|
||||
},
|
||||
init() {
|
||||
this.setColumnNum();
|
||||
this.setDefaultSelector();
|
||||
this.setColumnData();
|
||||
this.setSelectValue();
|
||||
},
|
||||
// 获取默认选中列下标
|
||||
setDefaultSelector() {
|
||||
// 如果没有传入默认选中的值,生成长度为columnNum,用0填充的数组
|
||||
this.defaultSelector = this.defaultValue.length == this.columnNum ? this.defaultValue : Array(this.columnNum).fill(0);
|
||||
this.lastSelectIndex = this.$u.deepClone(this.defaultSelector);
|
||||
},
|
||||
// 计算列数
|
||||
setColumnNum() {
|
||||
// 单列的列数为1
|
||||
if(this.mode == 'single-column') this.columnNum = 1;
|
||||
// 多列时,this.list数组长度就是列数
|
||||
else if(this.mode == 'mutil-column') this.columnNum = this.list.length;
|
||||
// 多列联动时,通过历遍this.list的第一个元素,得出有多少列
|
||||
else if(this.mode == 'mutil-column-auto') {
|
||||
let num = 1;
|
||||
let column = this.list;
|
||||
// 只要有元素并且第一个元素有children属性,继续历遍
|
||||
while(column[0][this.childName]) {
|
||||
column = column[0] ? column[0][this.childName] : {};
|
||||
num ++;
|
||||
}
|
||||
this.columnNum = num;
|
||||
}
|
||||
},
|
||||
// 获取需要展示在picker中的列数据
|
||||
setColumnData() {
|
||||
let data = [];
|
||||
this.selectValue = [];
|
||||
if(this.mode == 'mutil-column-auto') {
|
||||
// 获得所有数据中的第一个元素
|
||||
let column = this.list[this.defaultSelector.length ? this.defaultSelector[0] : 0];
|
||||
// 通过循环所有的列数,再根据设定列的数组,得出当前需要渲染的整个列数组
|
||||
for (let i = 0; i < this.columnNum; i++) {
|
||||
// 第一列默认为整个list数组
|
||||
if (i == 0) {
|
||||
data[i] = this.list;
|
||||
column = column[this.childName];
|
||||
} else {
|
||||
// 大于第一列时,判断是否有默认选中的,如果没有就用该列的第一项
|
||||
data[i] = column;
|
||||
column = column[this.defaultSelector[i]][this.childName];
|
||||
}
|
||||
}
|
||||
} else if(this.mode == 'single-column') {
|
||||
data[0] = this.list;
|
||||
} else {
|
||||
data = this.list;
|
||||
}
|
||||
this.columnData = data;
|
||||
},
|
||||
// 获取默认选中的值,如果没有设置defaultValue,就默认选中每列的第一个
|
||||
setSelectValue() {
|
||||
let tmp = null;
|
||||
for(let i = 0; i < this.columnNum; i++) {
|
||||
tmp = this.columnData[i][this.defaultSelector[i]];
|
||||
let data = {
|
||||
value: tmp ? tmp[this.valueName] : null,
|
||||
label: tmp ? tmp[this.labelName] : null
|
||||
};
|
||||
// 判断是否存在额外的参数,如果存在,就返回
|
||||
if(tmp && tmp.extra) data.extra = tmp.extra;
|
||||
this.selectValue.push(data)
|
||||
}
|
||||
},
|
||||
// 列选项
|
||||
columnChange(e) {
|
||||
let index = null;
|
||||
let columnIndex = e.detail.value;
|
||||
// 由于后面是需要push进数组的,所以需要先清空数组
|
||||
this.selectValue = [];
|
||||
if(this.mode == 'mutil-column-auto') {
|
||||
// 对比前后两个数组,寻找变更的是哪一列,如果某一个元素不同,即可判定该列发生了变化
|
||||
this.lastSelectIndex.map((val, idx) => {
|
||||
if (val != columnIndex[idx]) index = idx;
|
||||
});
|
||||
this.defaultSelector = columnIndex;
|
||||
for (let i = index + 1; i < this.columnNum; i++) {
|
||||
// 当前变化列的下一列的数据,需要获取上一列的数据,同时需要指定是上一列的第几个的children,再往后的
|
||||
// 默认是队列的第一个为默认选项
|
||||
this.columnData[i] = this.columnData[i - 1][i - 1 == index ? columnIndex[index] : 0][this.childName];
|
||||
// 改变的列之后的所有列,默认选中第一个
|
||||
this.defaultSelector[i] = 0;
|
||||
}
|
||||
// 在历遍的过程中,可能由于上一步修改this.columnData,导致产生连锁反应,程序触发columnChange,会有多次调用
|
||||
// 只有在最后一次数据稳定后的结果是正确的,此前的历遍中,可能会产生undefined,故需要判断
|
||||
columnIndex.map((item, index) => {
|
||||
let data = this.columnData[index][columnIndex[index]];
|
||||
let tmp = {
|
||||
value: data ? data[this.valueName] : null,
|
||||
label: data ? data[this.labelName] : null,
|
||||
};
|
||||
// 判断是否有需要额外携带的参数
|
||||
if(data && data.extra !== undefined) tmp.extra = data.extra;
|
||||
this.selectValue.push(tmp);
|
||||
|
||||
})
|
||||
// 保存这一次的结果,用于下次列发生变化时作比较
|
||||
this.lastSelectIndex = columnIndex;
|
||||
} else if(this.mode == 'single-column') {
|
||||
let data = this.columnData[0][columnIndex[0]];
|
||||
// 初始默认选中值
|
||||
let tmp = {
|
||||
value: data ? data[this.valueName] : null,
|
||||
label: data ? data[this.labelName] : null,
|
||||
};
|
||||
// 判断是否有需要额外携带的参数
|
||||
if(data && data.extra !== undefined) tmp.extra = data.extra;
|
||||
this.selectValue.push(tmp);
|
||||
} else if(this.mode == 'mutil-column') {
|
||||
// 初始默认选中值
|
||||
columnIndex.map((item, index) => {
|
||||
let data = this.columnData[index][columnIndex[index]];
|
||||
// 初始默认选中值
|
||||
let tmp = {
|
||||
value: data ? data[this.valueName] : null,
|
||||
label: data ? data[this.labelName] : null,
|
||||
};
|
||||
// 判断是否有需要额外携带的参数
|
||||
if(data && data.extra !== undefined) tmp.extra = data.extra;
|
||||
this.selectValue.push(tmp);
|
||||
})
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.$emit('input', false);
|
||||
},
|
||||
// 点击确定或者取消
|
||||
getResult(event = null) {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (this.moving) return;
|
||||
// #endif
|
||||
if (event) this.$emit(event, this.selectValue);
|
||||
this.close();
|
||||
},
|
||||
selectHandler() {
|
||||
this.$emit('click');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "uView-ui/libs/css/style.components.scss";
|
||||
|
||||
.u-select {
|
||||
|
||||
&__action {
|
||||
position: relative;
|
||||
line-height: $u-form-item-height;
|
||||
height: $u-form-item-height;
|
||||
|
||||
&__icon {
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 50%;
|
||||
transition: transform .4s;
|
||||
transform: translateY(-50%);
|
||||
z-index: 1;
|
||||
|
||||
&--reverse {
|
||||
transform: rotate(-180deg) translateY(50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__hader {
|
||||
&__title {
|
||||
color: $u-content-color;
|
||||
}
|
||||
}
|
||||
|
||||
&--border {
|
||||
border-radius: 6rpx;
|
||||
border-radius: 4px;
|
||||
border: 1px solid $u-form-item-border-color;
|
||||
}
|
||||
|
||||
&__header {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 80rpx;
|
||||
padding: 0 40rpx;
|
||||
}
|
||||
|
||||
&__body {
|
||||
width: 100%;
|
||||
height: 500rpx;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
|
||||
&__picker-view {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
&__item {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
color: $u-main-color;
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
<template>
|
||||
<view class="">
|
||||
<view
|
||||
class="u-steps"
|
||||
:style="{
|
||||
flexDirection: direction,
|
||||
}"
|
||||
>
|
||||
<view class="u-steps__item" :class="['u-steps__item--' + direction]" v-for="(item, index) in list" :key="index">
|
||||
<view
|
||||
class="u-steps__item__num"
|
||||
v-if="mode == 'number'"
|
||||
:style="{
|
||||
backgroundColor: current < index ? 'transparent' : activeColor,
|
||||
borderColor: current < index ? unActiveColor : activeColor,
|
||||
}"
|
||||
>
|
||||
<text
|
||||
v-if="current < index"
|
||||
:style="{
|
||||
color: current < index ? unActiveColor : activeColor,
|
||||
}"
|
||||
>
|
||||
{{ index + 1 }}
|
||||
</text>
|
||||
<u-icon v-else size="22" color="#ffffff" :name="icon"></u-icon>
|
||||
</view>
|
||||
<view
|
||||
class="u-steps__item__dot"
|
||||
v-if="mode == 'dot'"
|
||||
:style="{
|
||||
backgroundColor: index <= current ? activeColor : unActiveColor,
|
||||
}"
|
||||
></view>
|
||||
<view class="u-steps__item__icon" v-if="mode == 'icon'">
|
||||
|
||||
<u-icon v-if="current==-1&&index==0" size="48" :name="icon" :color="activeColor"></u-icon>
|
||||
<u-icon v-else size="48" :name="icon" :color="index <= current ? activeColor : unActiveColor"></u-icon>
|
||||
|
||||
</view>
|
||||
<text
|
||||
class="u-line-1"
|
||||
:style="[{
|
||||
color: index <= current ? activeColor : unActiveColor,
|
||||
},textStyle]"
|
||||
:class="['u-steps__item__text--' + direction]"
|
||||
>
|
||||
{{ item.name }}
|
||||
</text>
|
||||
<view class="u-steps__item__line" :class="['u-steps__item__line--' + mode]" v-if="index < list.length - 1">
|
||||
<u-line class="cu-line" :direction="direction" length="100%" :hair-line="false" :color="index <= current ? activeColor : unActiveColor"></u-line>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* steps 步骤条
|
||||
* @description 该组件一般用于完成一个任务要分几个步骤,标识目前处于第几步的场景。
|
||||
* @tutorial https://www.uviewui.com/components/steps.html
|
||||
* @property {String} mode 设置模式(默认dot)
|
||||
* @property {Array} list 数轴条数据,数组。具体见上方示例
|
||||
* @property {String} type type主题(默认primary)
|
||||
* @property {String} direction row-横向,column-竖向(默认row)
|
||||
* @property {Number String} current 设置当前处于第几步
|
||||
* @property {String} active-color 已完成步骤的激活颜色,如设置,type值会失效
|
||||
* @property {String} un-active-color 未激活的颜色,用于表示未完成步骤的颜色(默认#606266)
|
||||
* @example <u-steps :list="numList" active-color="#fa3534"></u-steps>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-steps',
|
||||
props: {
|
||||
// 步骤条的类型,dot|number|icon
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'dot',
|
||||
},
|
||||
// 步骤条的数据
|
||||
list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
// 主题类型, primary|success|info|warning|error
|
||||
type: {
|
||||
type: String,
|
||||
default: 'primary',
|
||||
},
|
||||
// 当前哪一步是激活的
|
||||
current: {
|
||||
type: [Number, String],
|
||||
default: 0,
|
||||
},
|
||||
// 激活步骤的颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: '#2979ff',
|
||||
},
|
||||
// 激活步骤文字的颜色
|
||||
activeTextColor: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 未激活的颜色
|
||||
unActiveColor: {
|
||||
type: String,
|
||||
default: '#909399',
|
||||
},
|
||||
// 未激活文字的颜色
|
||||
unActiveTextColor: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 自定义图标
|
||||
icon: {
|
||||
type: String,
|
||||
default: 'checkmark',
|
||||
},
|
||||
// step的排列方向,row-横向,column-竖向
|
||||
direction: {
|
||||
type: String,
|
||||
default: 'row',
|
||||
},
|
||||
textStyle:{
|
||||
type:Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
textColor() {
|
||||
return this.activeTextColor ? this.activeTextColor : this.activeColor;
|
||||
},
|
||||
unTextColor() {
|
||||
return this.unActiveTextColor ? this.unActiveTextColor : this.unActiveColor;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
@import 'uview-ui/libs/css/style.components.scss';
|
||||
|
||||
$u-steps-item-number-width: 40rpx;
|
||||
$u-steps-item-dot-width: 20rpx;
|
||||
|
||||
.cu-line{
|
||||
border-width:3rpx !important
|
||||
|
||||
}
|
||||
.u-steps {
|
||||
@include vue-flex;
|
||||
|
||||
.u-steps__item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
min-width: 100rpx;
|
||||
font-size: 26rpx;
|
||||
color: #8799a3;
|
||||
@include vue-flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&--row {
|
||||
@include vue-flex;
|
||||
flex-direction: column;
|
||||
|
||||
.u-steps__item__line {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
left: calc(50% + 10px);
|
||||
width:calc(100% - 20px);
|
||||
|
||||
&--dot {
|
||||
top: calc(#{$u-steps-item-dot-width} / 2);
|
||||
}
|
||||
|
||||
&--number {
|
||||
top: calc(#{$u-steps-item-number-width} / 2);
|
||||
}
|
||||
|
||||
&--icon {
|
||||
top: calc(#{$u-steps-item-number-width} / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--column {
|
||||
@include vue-flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
min-height: 120rpx;
|
||||
|
||||
.u-steps__item__line {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
height: 50%;
|
||||
top: 75%;
|
||||
|
||||
&--dot {
|
||||
left: calc(#{$u-steps-item-dot-width} / 2);
|
||||
}
|
||||
|
||||
&--number {
|
||||
left: calc(#{$u-steps-item-number-width} / 2);
|
||||
}
|
||||
|
||||
&--icon {
|
||||
left: calc(#{$u-steps-item-number-width} / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__num {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: $u-steps-item-number-width;
|
||||
height: $u-steps-item-number-width;
|
||||
border: 1px solid #8799a3;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: $u-steps-item-number-width;
|
||||
height: $u-steps-item-number-width;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__dot {
|
||||
width: $u-steps-item-dot-width;
|
||||
height: $u-steps-item-dot-width;
|
||||
@include vue-flex;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&__text--row {
|
||||
margin-top: 14rpx;
|
||||
}
|
||||
|
||||
&__text--column {
|
||||
margin-left: 14rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,381 @@
|
|||
<template>
|
||||
<view
|
||||
class="u-swiper-wrap"
|
||||
:style="{
|
||||
borderRadius: `${borderRadius}rpx`,
|
||||
}"
|
||||
>
|
||||
<swiper
|
||||
:current="elCurrent"
|
||||
@change="change"
|
||||
@animationfinish="animationfinish"
|
||||
:interval="interval"
|
||||
:circular="circular"
|
||||
:duration="duration"
|
||||
:autoplay="autoplay"
|
||||
:previous-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'"
|
||||
:next-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'"
|
||||
:style="{
|
||||
height: height + 'rpx',
|
||||
backgroundColor: bgColor,
|
||||
}"
|
||||
>
|
||||
<swiper-item class="u-swiper-item " v-for="(item, index) in list" :key="index">
|
||||
<view
|
||||
class="u-list-image-wrap relative "
|
||||
@tap.stop.prevent="listClick(index)"
|
||||
:class="[uCurrent != index ? 'u-list-scale' : '']"
|
||||
:style="{
|
||||
borderRadius: `${borderRadius}rpx`,
|
||||
transform: effect3d && uCurrent != index ? 'scaleY(0.9)' : 'scaleY(1)',
|
||||
margin: effect3d && uCurrent != index ? '0 20rpx' : 0,
|
||||
}"
|
||||
>
|
||||
<slot :data="item" :index="index">
|
||||
<image class="u-swiper-image " :src="item[name] || item" :mode="imgMode"></image>
|
||||
<view
|
||||
v-if="title && item.title"
|
||||
class="u-swiper-title u-line-1"
|
||||
:style="[
|
||||
{
|
||||
'padding-bottom': titlePaddingBottom,
|
||||
},
|
||||
titleStyle,
|
||||
]"
|
||||
>
|
||||
{{ item.title }}
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view
|
||||
class="u-swiper-indicator"
|
||||
:style="{
|
||||
top: indicatorPos == 'topLeft' || indicatorPos == 'topCenter' || indicatorPos == 'topRight' ? '12rpx' : 'auto',
|
||||
bottom: indicatorPos == 'bottomLeft' || indicatorPos == 'bottomCenter' || indicatorPos == 'bottomRight' ? '12rpx' : 'auto',
|
||||
justifyContent: justifyContent,
|
||||
padding: `0 ${effect3d ? '74rpx' : '24rpx'}`,
|
||||
}"
|
||||
>
|
||||
<block v-if="mode == 'rect'">
|
||||
<view
|
||||
class="u-indicator-item-rect"
|
||||
:class="{ 'u-indicator-item-rect-active': index == uCurrent }"
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
></view>
|
||||
</block>
|
||||
<block v-if="mode == 'dot'">
|
||||
<view
|
||||
class="u-indicator-item-dot"
|
||||
:class="{ 'u-indicator-item-dot-active': index == uCurrent }"
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
></view>
|
||||
</block>
|
||||
<block v-if="mode == 'round'">
|
||||
<view
|
||||
class="u-indicator-item-round"
|
||||
:class="{ 'u-indicator-item-round-active': index == uCurrent }"
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
></view>
|
||||
</block>
|
||||
<block v-if="mode == 'number'">
|
||||
<view class="u-indicator-item-number">{{ uCurrent + 1 }}/{{ list.length }}</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* swiper 轮播图
|
||||
* @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用
|
||||
* @tutorial https://www.uviewui.com/components/swiper.html
|
||||
* @property {Array} list 轮播图数据,见官网"基本使用"说明
|
||||
* @property {Boolean} title 是否显示标题文字,需要配合list参数,见官网说明(默认false)
|
||||
* @property {String} mode 指示器模式,见官网说明(默认round)
|
||||
* @property {String Number} height 轮播图组件高度,单位rpx(默认250)
|
||||
* @property {String} indicator-pos 指示器的位置(默认bottomCenter)
|
||||
* @property {Boolean} effect3d 是否开启3D效果(默认false)
|
||||
* @property {Boolean} autoplay 是否自动播放(默认true)
|
||||
* @property {String Number} interval 自动轮播时间间隔,单位ms(默认2500)
|
||||
* @property {Boolean} circular 是否衔接播放,见官网说明(默认true)
|
||||
* @property {String} bg-color 背景颜色(默认#f3f4f6)
|
||||
* @property {String Number} border-radius 轮播图圆角值,单位rpx(默认8)
|
||||
* @property {Object} title-style 自定义标题样式
|
||||
* @property {String Number} effect3d-previous-margin mode = true模式的情况下,激活项与前后项之间的距离,单位rpx(默认50)
|
||||
* @property {String} img-mode 图片的裁剪模式,详见image组件裁剪模式(默认aspectFill)
|
||||
* @event {Function} click 点击轮播图时触发
|
||||
* @example <u-swiper :list="list" mode="dot" indicator-pos="bottomRight"></u-swiper>
|
||||
*/
|
||||
export default {
|
||||
name: 'cu-swiper',
|
||||
props: {
|
||||
// 轮播图的数据,格式如:[{image: 'xxxx', title: 'xxxx'},{image: 'yyyy', title: 'yyyy'}],其中title字段可选
|
||||
list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
// 是否显示title标题
|
||||
title: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 用户自定义的指示器的样式
|
||||
indicator: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
// 圆角值
|
||||
borderRadius: {
|
||||
type: [Number, String],
|
||||
default: 8,
|
||||
},
|
||||
// 隔多久自动切换
|
||||
interval: {
|
||||
type: [String, Number],
|
||||
default: 3000,
|
||||
},
|
||||
// 指示器的模式,rect|dot|number|round
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'round',
|
||||
},
|
||||
// list的高度,单位rpx
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: 250,
|
||||
},
|
||||
// 指示器的位置,topLeft|topCenter|topRight|bottomLeft|bottomCenter|bottomRight
|
||||
indicatorPos: {
|
||||
type: String,
|
||||
default: 'bottomCenter',
|
||||
},
|
||||
// 是否开启缩放效果
|
||||
effect3d: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 3D模式的情况下,激活item与前后item之间的距离,单位rpx
|
||||
effect3dPreviousMargin: {
|
||||
type: [Number, String],
|
||||
default: 50,
|
||||
},
|
||||
// 是否自动播放
|
||||
autoplay: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 自动轮播时间间隔,单位ms
|
||||
duration: {
|
||||
type: [Number, String],
|
||||
default: 500,
|
||||
},
|
||||
// 是否衔接滑动,即到最后一张时接着滑动,是否自动切换到第一张
|
||||
circular: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 图片的裁剪模式
|
||||
imgMode: {
|
||||
type: String,
|
||||
default: 'aspectFill',
|
||||
},
|
||||
// 从list数组中读取的图片的属性名
|
||||
name: {
|
||||
type: String,
|
||||
default: 'image',
|
||||
},
|
||||
// 背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#f3f4f6',
|
||||
},
|
||||
// 初始化时,默认显示第几项
|
||||
current: {
|
||||
type: [Number, String],
|
||||
default: 0,
|
||||
},
|
||||
// 标题的样式,对象形式
|
||||
titleStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 如果外部的list发生变化,判断长度是否被修改,如果前后长度不一致,重置uCurrent值,避免溢出
|
||||
list(nVal, oVal) {
|
||||
if (nVal.length !== oVal.length) this.uCurrent = 0;
|
||||
},
|
||||
// 监听外部current的变化,实时修改内部依赖于此测uCurrent值,如果更新了current,而不是更新uCurrent,
|
||||
// 就会错乱,因为指示器是依赖于uCurrent的
|
||||
current(n) {
|
||||
this.uCurrent = n;
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uCurrent: this.current, // 当前活跃的swiper-item的index
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
justifyContent() {
|
||||
if (this.indicatorPos == 'topLeft' || this.indicatorPos == 'bottomLeft') return 'flex-start';
|
||||
if (this.indicatorPos == 'topCenter' || this.indicatorPos == 'bottomCenter') return 'center';
|
||||
if (this.indicatorPos == 'topRight' || this.indicatorPos == 'bottomRight') return 'flex-end';
|
||||
},
|
||||
titlePaddingBottom() {
|
||||
let tmp = 0;
|
||||
if (this.mode == 'none') return '12rpx';
|
||||
if (['bottomLeft', 'bottomCenter', 'bottomRight'].indexOf(this.indicatorPos) >= 0 && this.mode == 'number') {
|
||||
tmp = '60rpx';
|
||||
} else if (['bottomLeft', 'bottomCenter', 'bottomRight'].indexOf(this.indicatorPos) >= 0 && this.mode != 'number') {
|
||||
tmp = '40rpx';
|
||||
} else {
|
||||
tmp = '12rpx';
|
||||
}
|
||||
return tmp;
|
||||
},
|
||||
// 因为uni的swiper组件的current参数只接受Number类型,这里做一个转换
|
||||
elCurrent() {
|
||||
return Number(this.current);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
listClick(index) {
|
||||
this.$emit('click', index);
|
||||
},
|
||||
change(e) {
|
||||
let current = e.detail.current;
|
||||
this.uCurrent = current;
|
||||
// 发出change事件,表示当前自动切换的index,从0开始
|
||||
this.$emit('change', current);
|
||||
},
|
||||
// 头条小程序不支持animationfinish事件,改由change事件
|
||||
// 暂不监听此事件,因为不再给swiper绑定uCurrent属性
|
||||
animationfinish(e) {
|
||||
// #ifndef MP-TOUTIAO
|
||||
// this.uCurrent = e.detail.current;
|
||||
// #endif
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'uview-ui/libs/css/style.components.scss';
|
||||
|
||||
.u-swiper-wrap {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.u-swiper-image {
|
||||
width: 100%;
|
||||
will-change: transform;
|
||||
height: 100%;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
/* #ifdef H5 */
|
||||
pointer-events: none;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.u-swiper-indicator {
|
||||
padding: 0 24rpx;
|
||||
position: absolute;
|
||||
@include vue-flex;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.u-indicator-item-rect {
|
||||
width: 26rpx;
|
||||
height: 8rpx;
|
||||
margin: 0 6rpx;
|
||||
transition: all 0.5s;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.u-indicator-item-rect-active {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.u-indicator-item-dot {
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
margin: 0 6rpx;
|
||||
border-radius: 20rpx;
|
||||
transition: all 0.5s;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.u-indicator-item-dot-active {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.u-indicator-item-round {
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
margin: 0 6rpx;
|
||||
border-radius: 20rpx;
|
||||
transition: all 0.5s;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.u-indicator-item-round-active {
|
||||
width: 34rpx;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.u-indicator-item-number {
|
||||
padding: 6rpx 16rpx;
|
||||
line-height: 1;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 100rpx;
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.u-list-scale {
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.u-list-image-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
transition: all 0.5s;
|
||||
overflow: hidden;
|
||||
box-sizing: content-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.u-swiper-title {
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
font-size: 28rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.u-swiper-item {
|
||||
@include vue-flex;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<template>
|
||||
<view class="rounded-xs box-show" @tap.stop="$u.route('/pages/product_details/index', { skuId: goods.id,showShrough:showShrough })">
|
||||
<!-- <u-image width="100%" height="345rpx" :src="goods.cover" :lazy-load="true"></u-image> -->
|
||||
<cu-image :borderTopLeftRadius="24" :borderTopRightRadius="24" width="100%" height="345rpx" :src="`${goods.cover}?x-oss-process=image/resize,h_375,m_lfit`" :lazy-load="true"></cu-image>
|
||||
<view class="px-base">
|
||||
<view class="text-txBase line-2 mt-10rpx">{{ goods.name }}</view>
|
||||
<view class="text-md text-txBase flex items-center justify-between">
|
||||
<view>¥{{ goods.sell_price }}</view>
|
||||
<block v-if="showShrough">
|
||||
<view v-if="goods['market_price']!=undefined && goods.market_price" class="fontFam line-through text-txGray text-20rpx">¥{{goods.market_price}}</view>
|
||||
</block>
|
||||
</view>
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="flex items-center">
|
||||
<view class="text-lg -mt-5rpx text-txSvip">¥{{ goods.vip_price }}</view>
|
||||
<image class="w-58rpx h-58rpx ml-10rpx" src="/static/svg/svip.svg" mode=""></image>
|
||||
</view>
|
||||
<!-- 收藏按钮 -->
|
||||
<view v-if="isCollection">
|
||||
<u-icon size="36" color="#f0ad4e" name="star-fill"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
//obj[key]==undefined
|
||||
export default {
|
||||
props: {
|
||||
//是否显示收藏
|
||||
isCollection: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
//数据
|
||||
goods: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
//是否显示店铺直通车才显示删除的市场价格
|
||||
showShrough:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.box-show {
|
||||
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.fontFam{
|
||||
/* #ifdef APP-PLUS */
|
||||
font-family: "宋体";
|
||||
/* #endif */
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<u-waterfall ref="waterfall" v-model="goodsList">
|
||||
<template v-slot:left="{ leftList }">
|
||||
<view v-for="(item, index) in leftList" :key="index" class="px-rowSm mb-base">
|
||||
<goods-item :goods="item"></goods-item>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:right="{ rightList }">
|
||||
<view v-for="(item, index) in rightList" :key="index" class="px-rowSm mb-base">
|
||||
<goods-item :goods="item"></goods-item>
|
||||
</view>
|
||||
</template>
|
||||
</u-waterfall>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
goodsList:[]
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
list:{
|
||||
deep:true,
|
||||
immediate:true,
|
||||
handler(e){
|
||||
this.goodsList = this.$u.deepClone(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
clear(){
|
||||
this.$refs.waterfall.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
<template>
|
||||
<view class="root" :style="{width,height}">
|
||||
<image :style="{width,height}" class="posterImg" :src="posterUrl" mode="aspectFit"></image>
|
||||
<view :style="{width,height}" @tap="state=!state" class="box">
|
||||
<image class="playIcon" src="/static/images/icon_play.png" mode="widthFix"></image>
|
||||
</view>
|
||||
<video :id="videoId" :style="{height,width:state?'750rpx':'1rpx'}" @pause="state=0" @timeupdate="timeupdate" @fullscreenchange="fullscreenchange" class="video" :src="url"></video>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
computed:{
|
||||
posterUrl(){
|
||||
if(this.poster) return this.poster
|
||||
return this.url + '?x-oss-process=video/snapshot,t_'+(parseInt(this.currentTime*1000))+',f_jpg,w_800,m_fast'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.videoId = Date.now() + Math.ceil(Math.random()*10000000)+"";
|
||||
},
|
||||
mounted() {
|
||||
this.VideoContext = uni.createVideoContext(this.videoId)
|
||||
},
|
||||
methods:{
|
||||
fullscreenchange(e){
|
||||
this.state = e.detail.fullScreen
|
||||
},
|
||||
timeupdate(e){
|
||||
this.duration = e.detail.duration
|
||||
this.currentTime = e.detail.currentTime
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
state(state, oldValue) {
|
||||
//console.log(state,'state');
|
||||
if(!state){
|
||||
this.VideoContext.pause()
|
||||
}else{
|
||||
this.VideoContext.play()
|
||||
setTimeout(()=>{
|
||||
this.VideoContext.requestFullScreen({direction:this.direction})
|
||||
},10)
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
VideoContext:{},
|
||||
state:false,
|
||||
currentTime:0,
|
||||
duration:0,
|
||||
videoId:''
|
||||
};
|
||||
},
|
||||
props: {
|
||||
poster: {
|
||||
type: [String,Boolean],
|
||||
default(){
|
||||
return ''
|
||||
}
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default(){
|
||||
return ''
|
||||
}
|
||||
},
|
||||
direction: {
|
||||
type: Number,
|
||||
default(){
|
||||
return 0
|
||||
}
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default(){
|
||||
return "750rpx";
|
||||
}
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default(){
|
||||
return "450rpx";
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.root{
|
||||
position:relative;
|
||||
width: 750rpx;
|
||||
height: 300px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.posterImg,.video,.box{
|
||||
display: flex;
|
||||
width: 750rpx;
|
||||
height: 300px;
|
||||
//border: solid 1px red;absolute
|
||||
position:absolute;
|
||||
}
|
||||
.video{
|
||||
margin-left: -2000px;
|
||||
}
|
||||
.posterImg{
|
||||
//border: solid red 1px;
|
||||
}
|
||||
.box{
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.playIcon{
|
||||
width: 100rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<template>
|
||||
<view :class="'loading ' + ( type == 'flex' ? 'flex' : '' )" :style="{backgroundColor, }">
|
||||
<u-loading mode="circle" :size="size" :color="color"></u-loading>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'fixed'
|
||||
},
|
||||
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: '#fff'
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 40
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.loading {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loading.flex {
|
||||
position: static;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<view class="w-full">
|
||||
<view class=" fixed left-0 right-0 z-50 nav-shadow w-full h-6rpx"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name:"nav-shadow",
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.nav-shadow{
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 2rpx 10rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
<template>
|
||||
<view class="mark" :class="[show_key ? '' : 'hidden']">
|
||||
<view class="kong"></view>
|
||||
<!-- 信息框 -->
|
||||
<view class="msg">
|
||||
<!-- 关闭按钮 -->
|
||||
<view class="px-30rpx py-20rpx">
|
||||
<u-icon name="close" @tap="closeFuc"> </u-icon>
|
||||
</view>
|
||||
<view class="title"> {{title}} </view>
|
||||
<slot> </slot>
|
||||
<view class="pswBox">
|
||||
<view v-for="(item, index) in 6" :key="index" class="content_item">{{ password[index] ? '●' : '' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 数字键盘 -->
|
||||
<view class="numeric">
|
||||
<block v-for="item in num" :key="item">
|
||||
<view class="num" :class="item == 10 ? 'amend1' : item == 12 ? 'amend3 iconfont' : ''" @tap="press({ num: item })">
|
||||
<u-icon size="36" v-if="item == 12" name="backspace"></u-icon>
|
||||
<text v-else>{{ item == 10 ? '' : item == 11 ? '0' : item == 12 ? '' : item }}</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
title:{
|
||||
type:String,
|
||||
default:'请输入支付密码'
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
show_key:{
|
||||
get(){
|
||||
return this.value
|
||||
},
|
||||
set(val){
|
||||
this.$emit('input',val)
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
num: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
|
||||
password: '',
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
press(obj) {
|
||||
let num = obj.num;
|
||||
if (obj.num == 10) {
|
||||
console.log('我是10我什么都不干');
|
||||
} else if (obj.num == 12) {
|
||||
this.password = this.password.slice(0, this.password.length - 1);
|
||||
} else if (obj.num == 11) {
|
||||
num = '0';
|
||||
this.password += num;
|
||||
} else {
|
||||
this.password += num;
|
||||
}
|
||||
if (this.password.length == 6) {
|
||||
this.$emit('getPassword', this.password);
|
||||
this.password = '';
|
||||
}
|
||||
},
|
||||
// 关闭支付页面
|
||||
closeFuc() {
|
||||
this.show_key = false
|
||||
this.$emit('closeFuc', false);
|
||||
},
|
||||
// 找回密码
|
||||
forgetFuc() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/mine/myWallet/changezfPwd/changezfPwd',
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.mark {
|
||||
width: 750upx;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
padding: 0 0 500upx 0;
|
||||
position: fixed;
|
||||
top: 0upx;
|
||||
left: 0upx;
|
||||
z-index: 10027;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.kong {
|
||||
width: 750upx;
|
||||
height: 250upx;
|
||||
}
|
||||
.msg {
|
||||
width: 550upx;
|
||||
height: 450upx;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
border-radius: 20upx;
|
||||
margin: 0 auto;
|
||||
animation: msgBox 0.2s linear;
|
||||
}
|
||||
@keyframes msgBox {
|
||||
0% {
|
||||
transform: translateY(50%);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
transform: translateY(25%);
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0%);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes numBox {
|
||||
0% {
|
||||
transform: translateY(50%);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
transform: translateY(25%);
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0%);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.msg > .img {
|
||||
padding: 20upx 0 0 20upx;
|
||||
font-size: 40upx;
|
||||
}
|
||||
.msg > .title {
|
||||
width: 100%;
|
||||
height: 100upx;
|
||||
line-height: 100upx;
|
||||
font-weight: 500;
|
||||
font-size: 36upx;
|
||||
text-align: center;
|
||||
}
|
||||
.msg > .subTitle {
|
||||
width: 100%;
|
||||
height: 50upx;
|
||||
line-height: 50upx;
|
||||
font-weight: 400;
|
||||
font-size: 32upx;
|
||||
text-align: center;
|
||||
}
|
||||
.pswBox {
|
||||
width: 80%;
|
||||
height: 80upx;
|
||||
margin: 50upx auto 0;
|
||||
display: flex;
|
||||
}
|
||||
.content_item {
|
||||
flex: 2;
|
||||
text-align: center;
|
||||
line-height: 80upx;
|
||||
border: 1px solid #d6d6d6;
|
||||
border-right: 0upx solid;
|
||||
}
|
||||
.content_item:nth-child(1) {
|
||||
border-radius: 10upx 0 0 10upx;
|
||||
}
|
||||
.content_item:nth-child(6) {
|
||||
border-right: 1px solid #d6d6d6;
|
||||
border-radius: 0 10upx 10upx 0;
|
||||
}
|
||||
.numeric {
|
||||
width: 750upx;
|
||||
height: 470upx;
|
||||
border: 1upx solid;
|
||||
position: fixed;
|
||||
z-index: 98;
|
||||
bottom: 0upx;
|
||||
background-color: #fff;
|
||||
animation: msgBox 0.2s linear;
|
||||
}
|
||||
.num {
|
||||
width: 225upx;
|
||||
height: 90upx;
|
||||
font-size: 42upx;
|
||||
font-weight: 500;
|
||||
line-height: 90upx;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
margin: 12upx 12upx 0 12upx;
|
||||
display: inline-block;
|
||||
border: 1upx solid #e4e7ed;
|
||||
border-radius: 10upx;
|
||||
background-color: #ffffff;
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
}
|
||||
.forget {
|
||||
font-size: 28upx;
|
||||
font-weight: 500;
|
||||
color: #3d84ea;
|
||||
text-align: center;
|
||||
line-height: 80upx;
|
||||
}
|
||||
.amend1 {
|
||||
border: 1upx solid #e4e7ed;
|
||||
background-color: #cccfd6;
|
||||
}
|
||||
.amend3 {
|
||||
border: 1upx solid #e4e7ed;
|
||||
background-color: #e4e7ed;
|
||||
line-height: 80rpx;
|
||||
}
|
||||
/* .amend11{
|
||||
position: absolute;
|
||||
top: 313upx;
|
||||
left: 0upx;
|
||||
background-color: #CCCFD6;
|
||||
border: 1upx solid #FF0000;
|
||||
}
|
||||
.amend1{
|
||||
height: 100upx !important;
|
||||
position: absolute;
|
||||
top: 306upx;
|
||||
left: 0upx;
|
||||
z-index: 99;
|
||||
background-color: #CCCFD6;
|
||||
border: 2upx solid #CCCFD6;
|
||||
}
|
||||
.amend2{
|
||||
position: absolute;
|
||||
top: 306upx;
|
||||
left: 250upx;
|
||||
z-index: 99;
|
||||
}
|
||||
.amend3{
|
||||
position: absolute;
|
||||
top: 306upx;
|
||||
left: 500upx;
|
||||
z-index: 99;
|
||||
font-size: 60upx;
|
||||
border: 0upx;
|
||||
background-color: #CCCFD6;
|
||||
} */
|
||||
</style>
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
<text :style="{color, 'font-weight': weight}" :class="(lineThrough ? 'line-through' : '') + ' price-format'">
|
||||
<text v-if="showSubscript" :style="{'font-size': subscriptSize + 'rpx', 'margin-right': '2rpx'}">¥</text>
|
||||
<text :style="{'font-size': firstSize + 'rpx', 'margin-right': '1rpx'}">{{priceSlice.first}}</text>
|
||||
<text v-if="priceSlice.second" :style="{'font-size': secondSize + 'rpx'}">.{{priceSlice.second}}</text>
|
||||
</text>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
priceSlice: {
|
||||
}
|
||||
};
|
||||
},
|
||||
components: {},
|
||||
props: {
|
||||
firstSize: {
|
||||
type: [String, Number],
|
||||
default: 28
|
||||
},
|
||||
secondSize: {
|
||||
type: [String, Number],
|
||||
default: 28
|
||||
},
|
||||
color: {
|
||||
type: String
|
||||
},
|
||||
weight: {
|
||||
type:[String, Number] ,
|
||||
default: 400
|
||||
},
|
||||
price: {
|
||||
type: [String, Number],
|
||||
default: ""
|
||||
},
|
||||
showSubscript: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
subscriptSize: {
|
||||
type: [String, Number],
|
||||
default: 28
|
||||
},
|
||||
lineThrough: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.priceFormat()
|
||||
},
|
||||
watch: {
|
||||
price(val) {
|
||||
this.priceFormat()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
priceFormat() {
|
||||
let {
|
||||
price
|
||||
} = this;
|
||||
let priceSlice = {}
|
||||
if(price !== null && price !== '' && price !== undefined) {
|
||||
price = parseFloat(price);
|
||||
price = String(price).split('.');
|
||||
priceSlice.first = price[0];
|
||||
priceSlice.second = price[1];
|
||||
this.priceSlice = priceSlice
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.price-format {
|
||||
font-family: Avenir, SourceHanSansCN, PingFang SC, Arial, Hiragino Sans GB, Microsoft YaHei, sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
<template>
|
||||
<view class="">
|
||||
<cu-swiper indicator-pos="bottomRight" mode="number" height="750" border-radius="0" :list="urls">
|
||||
<template slot-scope="scope">
|
||||
<view class="w-full h-full relative" @tap="previewImage(scope.index)">
|
||||
<block v-if="scope.data.type=='video'">
|
||||
<!-- #ifdef H5 || MP-WEIXIN -->
|
||||
<video id="myVideo" class="w-full h-full" :enable-progress-gesture="false" :controls="showControls"
|
||||
:autoplay="true" :show-progress="true" :show-fullscreen-btn="showControls" :src="scope.data.video"
|
||||
:show-center-play-btn="false" @error="videoErrorCallback" :show-play-btn="showControls"
|
||||
:poster="scope.data.image"
|
||||
@play="showPlay = false" @ended="playEnd" @fullscreenchange="fullscreenchange">
|
||||
</video>
|
||||
<image v-show="showPlay" @tap.stop="play" src="/static/images/icon_play.png" class="w-90rpx h-90rpx play-icon"></image>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<j-video :url="scope.data.video" height="750rpx" width="750rpx" :poster="scope.data.image"></j-video>
|
||||
<!-- #endif -->
|
||||
</block>
|
||||
<u-image v-else width="100%" height="100%" :src="scope.data.image"></u-image>
|
||||
</view>
|
||||
</template>
|
||||
</cu-swiper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
currentSwiper: 0,
|
||||
urls: [],
|
||||
showPlay: true,
|
||||
showControls: false,
|
||||
autoplay: true
|
||||
};
|
||||
},
|
||||
|
||||
components: {
|
||||
videoUrl(){
|
||||
return this.list.find(e=>e.type=='video')?.video ?? ''
|
||||
}
|
||||
},
|
||||
props: {
|
||||
list:{
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
circular: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
interval: {
|
||||
type: Number,
|
||||
default: 3000,
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 500,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
list: {
|
||||
handler(val) {
|
||||
this.urls = val
|
||||
if(val.findIndex(e=>e.type=='video')>=0){
|
||||
this.autoplay = false
|
||||
this.$nextTick(() => {
|
||||
this.videoContext = uni.createVideoContext('myVideo', this)
|
||||
})
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
swiperChange(e) {
|
||||
this.currentSwiper = e.detail.current
|
||||
},
|
||||
videoErrorCallback(err) {
|
||||
console.log('err==>', err)
|
||||
},
|
||||
playEnd() {
|
||||
this.showPlay = true;
|
||||
},
|
||||
previewImage(current) {
|
||||
|
||||
let index = current
|
||||
const urls = this.urls.map(e=>Object.assign(e,{url:e.image}))
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
wx.previewMedia({
|
||||
current,
|
||||
sources: urls,
|
||||
});
|
||||
//#endif
|
||||
// #ifdef H5 || APP-PLUS
|
||||
if (this.videoUrl) {
|
||||
index = current - 1
|
||||
}
|
||||
if (urls[current].type == "video") {
|
||||
this.videoContext.requestFullScreen()
|
||||
} else {
|
||||
uni.previewImage({
|
||||
index,
|
||||
urls: this.list.filter(({type})=>type == 'image').map(({image})=>image)
|
||||
})
|
||||
}
|
||||
|
||||
//#endif
|
||||
},
|
||||
play() {
|
||||
this.videoContext.play()
|
||||
},
|
||||
fullscreenchange(e) {
|
||||
const {
|
||||
fullScreen
|
||||
} = e.detail
|
||||
|
||||
!fullScreen && this.videoContext.pause()
|
||||
this.showControls = fullScreen ? true : false
|
||||
this.showPlay = fullScreen ? false : true
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.play-icon{
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<template>
|
||||
<view class="box-content" :class="customClass"><slot></slot></view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
customClass: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
bottom: { // rpx
|
||||
type: String | Number,
|
||||
default: 0
|
||||
},
|
||||
},
|
||||
computed:{
|
||||
safeBottom(){
|
||||
const systemInfo = uni.getSystemInfoSync()
|
||||
|
||||
return systemInfo.screenHeight - systemInfo.safeArea.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.box-content {
|
||||
padding-bottom: var(--window-bottom);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
<template>
|
||||
<view class="flex w-full items-center">
|
||||
<u-input type="number" :clearable="false" class="w-full flex-1 ml-30rpx flex-1" v-model="val" :maxlength="maxLength" :placeholder="placeholder"></u-input>
|
||||
<view>
|
||||
<view @tap="run" class="bg-hex-F4F4F4 rounded-full px-32rpx py-12rpx text-primary text-26rpx"> {{ text }} </view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '验证码',
|
||||
},
|
||||
maxLength: {
|
||||
type: Number,
|
||||
},
|
||||
second: {
|
||||
type: Number,
|
||||
default: 60,
|
||||
},
|
||||
btnText: {
|
||||
type: String,
|
||||
default: '获取验证码',
|
||||
},
|
||||
disabled: {
|
||||
type: [Boolean, Number],
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
time: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
text() {
|
||||
return this.time > 0 ? this.time + `秒重新获取` : this.btnText;
|
||||
},
|
||||
val: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(e) {
|
||||
this.$emit('input', e);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getSmsCode() {
|
||||
this.$emit('change');
|
||||
},
|
||||
run() {
|
||||
if(this.time>0) return
|
||||
this.$emit('run');
|
||||
},
|
||||
start() {
|
||||
this.time = this.second;
|
||||
this.timer();
|
||||
},
|
||||
stop() {
|
||||
this.time = 0;
|
||||
this.timer();
|
||||
},
|
||||
timer() {
|
||||
if (this.time > 0) {
|
||||
this.time--;
|
||||
setTimeout(this.timer, 1000);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
<template>
|
||||
<cu-popup v-model="show" mode="bottom" border-radius="20" closeable close-icon-size="24">
|
||||
<view class="px-40rpx mt-40rpx">
|
||||
<view class="flex py-base">
|
||||
<image class="flex-none rounded-20rpx w-200rpx h-200rpx" :src="sku.cover" mode="aspectFill"></image>
|
||||
<view class="flex flex-col justify-end ml-40rpx">
|
||||
<view class="font-bold text-hex-FF0200 mr-36rpx text-52rpx"> {{ sku.sell_price }} </view>
|
||||
<view class="mt-10rpx text-28rpx"> 会员价:{{ sku.vip_price }} </view>
|
||||
<view class="text-hex-999999 text-26rpx mb-20rpx leading-34rpx mt-10rpx">
|
||||
{{ `剩余库存 ${sku.stock}` }}
|
||||
</view>
|
||||
<view class="text-hex-333333 text-26rpx leading-34rpx"> 已选择:{{ activeSkuText }};数量:{{ quotaUsed }} </view>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view scroll-y="true" class="max-h-900rpx">
|
||||
<view>
|
||||
<view v-for="(item, index) in specs" :key="index">
|
||||
<view class="font-semibold py-30rpx text-333333 text-30rpx"> {{ item.name }} </view>
|
||||
<view class="-mr-24rpx">
|
||||
<view class="inline-block mr-24rpx mb-16rpx" v-for="(spce,index2) in item.items" :key="index2">
|
||||
<u-tag
|
||||
@tap="onChangeSku(spce, item.items)"
|
||||
:text="spce.name"
|
||||
class="cu-tag"
|
||||
:disabled="spce.sku_id == 0"
|
||||
:color="spce.selected ? '#FF0000' : '#333333'"
|
||||
:bg-color="spce.selected ? '#FDF2EC' : '#F8F8F8'"
|
||||
:border-color="spce.selected ? '#FDF2EC' : '#F8F8F8'"
|
||||
shape="circle"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="flex items-center">
|
||||
<view class="font-semibold py-30rpx text-333333 text-28rpx mr-40rpx"> 数量 </view>
|
||||
<view class="text-hex-FF0000 text-24rpx" v-if="sku.stock == 0"> 库存不足 </view>
|
||||
</view>
|
||||
<view>
|
||||
<u-number-box v-model="stepper" :min="1" :max="sku.stock" @change="onChangeNumber"></u-number-box>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="py-40rpx">
|
||||
<slot name="footer">
|
||||
<view class="flex w-full">
|
||||
<view
|
||||
:disabled="disabled"
|
||||
v-if="showCart"
|
||||
class="flex-1 btn text-center text-white rounded-full bg-primary h-80rpx leading-80rpx text-32rpx mr-30rpx"
|
||||
@tap="onClick('add-cart')"
|
||||
>
|
||||
<text>{{ cartBtnText }}</text>
|
||||
</view>
|
||||
<view
|
||||
v-if="showBuy"
|
||||
:disabled="disabled"
|
||||
class="flex-1 btn text-center text-white rounded-full bg-bgSubtitle h-80rpx leading-80rpx text-32rpx"
|
||||
@tap="onClick('buy-clicked')"
|
||||
>
|
||||
<text>{{ buyBtnText }}</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="showConfirm"
|
||||
:disabled="disabled"
|
||||
class="flex-1 btn text-center text-white rounded-full bg-bgSubtitle h-80rpx leading-80rpx text-32rpx"
|
||||
@tap="onClick('confirm')"
|
||||
>
|
||||
<text>确定</text>
|
||||
</view>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
</cu-popup>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
specs: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
showCart: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showBuy: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
cartBtnText: {
|
||||
type: String,
|
||||
default: '加入购物车',
|
||||
},
|
||||
buyBtnText: {
|
||||
type: String,
|
||||
default: '立即购买',
|
||||
},
|
||||
showConfirm: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
sku: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
//数量
|
||||
quotaUsed: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
show: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(e) {
|
||||
this.$emit('input', e);
|
||||
},
|
||||
},
|
||||
//已选中sku
|
||||
activeSku() {
|
||||
const sku = this.specs.reduce((arr, pr) => {
|
||||
if (pr.items.some((e) => e.selected)) {
|
||||
const item = pr.items.find((e) => e.selected);
|
||||
arr.push(item);
|
||||
}
|
||||
return arr;
|
||||
}, []);
|
||||
return sku;
|
||||
},
|
||||
//选中sku文本
|
||||
activeSkuText() {
|
||||
return this.activeSku.map((e) => e.name).join(',');
|
||||
},
|
||||
//是选完
|
||||
isSelect() {
|
||||
return this.activeSku.length == this.specs.length;
|
||||
},
|
||||
// 是否超库
|
||||
isOutStock() {
|
||||
return this.stepper > this.sku.stock;
|
||||
},
|
||||
// 超库存禁用
|
||||
disabled(){
|
||||
return this.sku.stock < this.quotaUsed
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
stepper: this.quotaUsed,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
//数量改变
|
||||
onChangeNumber(e) {
|
||||
this.$emit('stepper-change', e.value);
|
||||
},
|
||||
//规格切换
|
||||
onChangeSku(e, skus) {
|
||||
if (this.loading) return;
|
||||
if (e.sku_id > 0) {
|
||||
skus.forEach((el) => (el.selected = false));
|
||||
e.selected = true;
|
||||
this.stepper = 1;
|
||||
this.$emit('sku-selected', e);
|
||||
}
|
||||
},
|
||||
|
||||
//加入购物车
|
||||
onClick(type) {
|
||||
if (!this.isSelect) return this.$u.toast('请选择规格');
|
||||
if (this.isOutStock) return this.$u.toast('超出库存');
|
||||
this.$emit(type, this.sku);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.cu-tag{
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<view :class="'trigonometry ' + (direction === 'up' ? 'up' : '') + ' ' + (size === 'small' ? 'small' : '')" :style="'color:' + color + ';opacity: ' + opacity"></view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
|
||||
components: {},
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
direction: {
|
||||
type: String
|
||||
},
|
||||
size: {
|
||||
type: String
|
||||
},
|
||||
opacity: {
|
||||
type: String,
|
||||
default: '0.8'
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.trigonometry {
|
||||
border-color: transparent transparent currentcolor currentcolor;
|
||||
border-style: solid;
|
||||
border-width: 3px;
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
opacity: .8;
|
||||
margin: -3px 10rpx 0;
|
||||
}
|
||||
|
||||
.up {
|
||||
margin-top: 1rpx;
|
||||
-webkit-transform: rotate(135deg);
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
.small {
|
||||
border-width: 2px;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.small.up {
|
||||
margin-top: 2px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<view>
|
||||
<u-action-sheet :list="list" @tap="uploadImge" v-model="show"></u-action-sheet>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
album,
|
||||
camera
|
||||
} from '@/utils/author.js'
|
||||
import uploadImage from '@/utils/ossutil/uploadFile.js';
|
||||
export default {
|
||||
name: "upload",
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: [{
|
||||
text: '拍摄',
|
||||
fontSize: 28,
|
||||
color: '#383838'
|
||||
},
|
||||
{
|
||||
text: '从相册选择',
|
||||
fontSize: 28,
|
||||
color: '#383838'
|
||||
}
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
show: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(e) {
|
||||
this.$emit('input', e);
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async uploadImge(index) {
|
||||
//相机
|
||||
if (index == 0) {
|
||||
// #ifdef APP-PLUS
|
||||
// let result = await camera()
|
||||
// if (!result) return
|
||||
// #endif
|
||||
this.openImage('camera')
|
||||
} else {
|
||||
//相册
|
||||
// #ifdef APP-PLUS
|
||||
// let result = await album()
|
||||
// if (!result) return
|
||||
// #endif
|
||||
this.openImage('album')
|
||||
}
|
||||
},
|
||||
//拍照
|
||||
openImage(val) {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sourceType: [val],
|
||||
crop:{
|
||||
width:'80',
|
||||
height:'80',
|
||||
},
|
||||
success: (res) => {
|
||||
this.$emit('result',res.tempFilePaths[0])
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
import store from '@/store'
|
||||
|
||||
import routeMixin from '@/mixin/router.mixin'
|
||||
Vue.mixin(routeMixin)
|
||||
import appMixin from '@/mixin/app.mixin'
|
||||
Vue.mixin(appMixin)
|
||||
|
||||
import uView from "uview-ui";
|
||||
Vue.use(uView);
|
||||
|
||||
import * as filters from "@/utils/filters.js";
|
||||
// 注册全局实用程序过滤器.
|
||||
Object.keys(filters).forEach(key => {
|
||||
Vue.filter(key, filters[key]);
|
||||
});
|
||||
import { http } from '@/utils/request'
|
||||
Vue.config.productionTip = false
|
||||
App.mpType = 'app'
|
||||
Vue.prototype.$api = http
|
||||
|
||||
// #ifdef H5
|
||||
if(process.env.NODE_ENV === 'development'){
|
||||
const vconsole = require('vconsole')
|
||||
Vue.prototype.$vconsole = new vconsole() // 使用vconsole
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
const app = new Vue({
|
||||
store,
|
||||
...App
|
||||
})
|
||||
|
||||
app.$mount()
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
{
|
||||
"name" : "商城",
|
||||
"appid" : "__UNI__EC894CF",
|
||||
"description" : "",
|
||||
"versionName" : "1.3.4",
|
||||
"versionCode" : 134,
|
||||
"transformPx" : false,
|
||||
"app-plus" : {
|
||||
/* 5+App特有相关 */
|
||||
"usingComponents" : true,
|
||||
"splashscreen" : {
|
||||
"alwaysShowBeforeRender" : true,
|
||||
"waiting" : true,
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
"privacy" : {
|
||||
"prompt" : "template",
|
||||
"template" : {
|
||||
//prompt取值为template时有效,用于配置模板提示框上显示的内容
|
||||
"title" : "服务协议和隐私政策",
|
||||
"message" : "请你务必审慎阅读、充分理解“服务协议”和“隐私协议”各条款,包括但不限于:为你提供商品购买等服务,我们需要收集你的设备信息等个人信息。你可以在“设置”中查看、变更、删除个人信息并管理你的授权。你可阅读<a href=\"https://api.zichunsheng.cn/h5/articles/2\">《用户协议》</a>及<a href=\"https://api.zichunsheng.cn/h5/articles/3\">《隐私政策》</a>,<br/> 了解详细信息。如你同意,请点击“同意”开始接受我们的服务",
|
||||
"buttonAccept" : "同意", //继续下一步
|
||||
"buttonRefuse" : "暂不使用" //退出下载
|
||||
}
|
||||
},
|
||||
"compatible" : {
|
||||
"ignoreVersion" : true //true表示忽略版本检查提示框
|
||||
},
|
||||
"safearea" : {
|
||||
//安全区域配置,仅iOS平台生效
|
||||
"bottom" : {
|
||||
// 底部安全区域配置
|
||||
"offset" : "none" // 底部安全区域偏移,"none"表示不空出安全区域,"auto"自动计算空出安全区域,默认值为"none"
|
||||
}
|
||||
},
|
||||
"modules" : {
|
||||
"Payment" : {},
|
||||
"iBeacon" : {},
|
||||
"SQLite" : {},
|
||||
"Push" : {},
|
||||
"Share" : {},
|
||||
"VideoPlayer" : {},
|
||||
"Maps" : {}
|
||||
},
|
||||
/* 模块配置 */
|
||||
"distribute" : {
|
||||
/* 应用发布信息 */
|
||||
"android" : {
|
||||
/* android打包配置 */
|
||||
"permissions" : [
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.BLUETOOTH\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.INTERNET\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.RECEIVE_USER_PRESENT\"/>",
|
||||
"<uses-permission android:name=\"android.permission.BLUETOOTH\"/>",
|
||||
"<uses-permission android:name=\"android.permission.BLUETOOTH_ADMIN\"/>"
|
||||
],
|
||||
"abiFilters" : [ "armeabi-v7a", "arm64-v8a" ]
|
||||
},
|
||||
"ios" : {
|
||||
"capabilities" : {
|
||||
"entitlements" : {
|
||||
"com.apple.developer.associated-domains" : [ "applinks:static-639df241-3e40-451e-9e00-2cf3bf79ce41.bspapp.com" ]
|
||||
}
|
||||
},
|
||||
"privacyDescription" : {
|
||||
"NSPhotoLibraryUsageDescription" : "因相关功能涉及图片的读取与写入,请在设置-子春生中开启相册权限",
|
||||
"NSPhotoLibraryAddUsageDescription" : "因相关功能涉及图片的读取与写入,请在设置-子春生中开启相册权限",
|
||||
"NSCameraUsageDescription" : "因相关功能涉及照相,请在设置-子春生中开启相机权限",
|
||||
"NSLocationWhenInUseUsageDescription" : "用户能快速知道当前位置",
|
||||
"NSSpeechRecognitionUsageDescription" : "语音播报"
|
||||
},
|
||||
"idfa" : false
|
||||
},
|
||||
/* ios打包配置 */
|
||||
"sdkConfigs" : {
|
||||
"ad" : {},
|
||||
"maps" : {
|
||||
"amap" : {
|
||||
"appkey_ios" : "2fb25034076d2f0ab550ab3ab8616ce1",
|
||||
"appkey_android" : "ab4fc873f9e4a3b4e9bd9c586ddb3a4e"
|
||||
}
|
||||
},
|
||||
"payment" : {
|
||||
"weixin" : {
|
||||
"__platform__" : [ "ios", "android" ],
|
||||
"appid" : "wx5b38851d7f7aeede",
|
||||
"UniversalLinks" : "https://ios.zichunsheng.cn/ulink/"
|
||||
},
|
||||
"alipay" : {
|
||||
"__platform__" : [ "ios", "android" ]
|
||||
}
|
||||
},
|
||||
"push" : {
|
||||
"unipush" : {}
|
||||
},
|
||||
"share" : {
|
||||
"weixin" : {
|
||||
"appid" : "wx5b38851d7f7aeede",
|
||||
"UniversalLinks" : "https://ios.zichunsheng.cn/ulink/"
|
||||
}
|
||||
},
|
||||
"speech" : {
|
||||
"baidu" : {
|
||||
"appid" : "",
|
||||
"apikey" : "",
|
||||
"secretkey" : ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"icons" : {
|
||||
"android" : {
|
||||
"hdpi" : "unpackage/res/icons/72x72.png",
|
||||
"xhdpi" : "unpackage/res/icons/96x96.png",
|
||||
"xxhdpi" : "unpackage/res/icons/144x144.png",
|
||||
"xxxhdpi" : "unpackage/res/icons/192x192.png"
|
||||
},
|
||||
"ios" : {
|
||||
"appstore" : "unpackage/res/icons/1024x1024.png",
|
||||
"ipad" : {
|
||||
"app" : "unpackage/res/icons/76x76.png",
|
||||
"app@2x" : "unpackage/res/icons/152x152.png",
|
||||
"notification" : "unpackage/res/icons/20x20.png",
|
||||
"notification@2x" : "unpackage/res/icons/40x40.png",
|
||||
"proapp@2x" : "unpackage/res/icons/167x167.png",
|
||||
"settings" : "unpackage/res/icons/29x29.png",
|
||||
"settings@2x" : "unpackage/res/icons/58x58.png",
|
||||
"spotlight" : "unpackage/res/icons/40x40.png",
|
||||
"spotlight@2x" : "unpackage/res/icons/80x80.png"
|
||||
},
|
||||
"iphone" : {
|
||||
"app@2x" : "unpackage/res/icons/120x120.png",
|
||||
"app@3x" : "unpackage/res/icons/180x180.png",
|
||||
"notification@2x" : "unpackage/res/icons/40x40.png",
|
||||
"notification@3x" : "unpackage/res/icons/60x60.png",
|
||||
"settings@2x" : "unpackage/res/icons/58x58.png",
|
||||
"settings@3x" : "unpackage/res/icons/87x87.png",
|
||||
"spotlight@2x" : "unpackage/res/icons/80x80.png",
|
||||
"spotlight@3x" : "unpackage/res/icons/120x120.png"
|
||||
}
|
||||
}
|
||||
},
|
||||
"splashscreen" : {
|
||||
"iosStyle" : "common",
|
||||
"androidStyle" : "common"
|
||||
}
|
||||
}
|
||||
},
|
||||
/* SDK配置 */
|
||||
"quickapp" : {},
|
||||
/* 快应用特有相关 */
|
||||
"mp-weixin" : {
|
||||
"appid" : "wx374270da1f0100da",
|
||||
"setting" : {
|
||||
"urlCheck" : false
|
||||
},
|
||||
"usingComponents" : true,
|
||||
"permission" : {
|
||||
"scope.userLocation" : {
|
||||
"desc" : "你的位置信息将用于小程序位置接口的效果展示"
|
||||
}
|
||||
},
|
||||
"optimization" : {
|
||||
"subPackages" : true
|
||||
}
|
||||
},
|
||||
"mp-alipay" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-baidu" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-toutiao" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-qq" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"_spaceID" : "639df241-3e40-451e-9e00-2cf3bf79ce41",
|
||||
"h5" : {
|
||||
"template" : "",
|
||||
"router" : {
|
||||
"mode" : "history"
|
||||
},
|
||||
"optimization" : {
|
||||
"treeShaking" : {
|
||||
"enable" : true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
|
||||
onLoad(option) {
|
||||
// #ifdef H5
|
||||
|
||||
// #endif
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isLogin', 'news_num']),
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import store from '@/store'
|
||||
|
||||
|
||||
const whiteList = [
|
||||
'/pages/login/index'
|
||||
]
|
||||
|
||||
export default {
|
||||
created() {
|
||||
|
||||
// 登录判断跳转
|
||||
this.$u.routeAuth = (...args) => {
|
||||
let url = args[0]
|
||||
if (typeof url !== 'string' && typeof url.url === 'string') {
|
||||
url = url.url
|
||||
}
|
||||
|
||||
if (store.getters.isLogin || whiteList.includes(url)) {
|
||||
this.$u.route(...args)
|
||||
} else {
|
||||
this.$u.route('/pages/login/index')
|
||||
}
|
||||
}
|
||||
|
||||
this.$u.routeLogin = (...args) => {
|
||||
uni.$u.throttle(this.$u.route('/pages/login/index'), 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
<template>
|
||||
<view class="flex flex-col w-full min-h-full" @tap="Lindex = -1">
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<view class="pb-200rpx flex-1">
|
||||
<view @longpress="long_press(index)" @tap.stop="Select(item)" class="relative " v-for="(item,index) in list"
|
||||
:key="index">
|
||||
<view class="px-base ">
|
||||
<view class="flex Border py-base items-center justify-between bg-white ">
|
||||
<view>
|
||||
<view class="flex items-center text-black">
|
||||
<block v-if="item.is_default">
|
||||
<image class="w-59rpx" src="/static/images/order/defalut-icon.png" mode="widthFix" />
|
||||
</block>
|
||||
<text class="text-txGray">{{item.zone}}</text>
|
||||
</view>
|
||||
<view class=" text-xl text-black my-14rpx"> {{item.address}} </view>
|
||||
<view class="text-lg text-txGray"> {{item.consignee}} {{item.telephone}} </view>
|
||||
</view>
|
||||
<view @tap.stop="editAddress(item)" >
|
||||
<u-icon color="#808080" name="edit-pen" size="36"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 弹窗 -->
|
||||
<view v-if="Lindex==index"
|
||||
class="flex text-md text-white items-center justify-end absolute top-0 right-0 left-0 w-full h-full z-50 bg-txBase bg-opacity-70">
|
||||
<view @tap.stop="set_default(item)" class="btn flex items-center justify-center flex-col bg-primary ">
|
||||
<view>设为</view>
|
||||
<view>默认</view>
|
||||
</view>
|
||||
<view @tap.stop="copy(item)" class="btn flex items-center justify-center flex-col text-txBase bg-white ">
|
||||
<view>复制</view>
|
||||
<view>地址</view>
|
||||
</view>
|
||||
<view @tap.stop="Del(index)" class="btn flex items-center justify-center flex-col bg-hex-D43030 ">
|
||||
<view>删除</view>
|
||||
<view>地址</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="px-base pb-60rpx ">
|
||||
<view class="login-btn" @tap.stop="$u.route('/pageA/new_address/index')"> +新建收货地址 </view>
|
||||
</view>
|
||||
<!-- 删除确认 -->
|
||||
<cu-modal v-model="show" @cancel="Lindex = -1" @confirm="confirm" confirm-color="#378264" show-cancel-button
|
||||
content="是否删除该地址"></cu-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniCopy from '@/utils/uni-copy'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isFirstLoading:true,
|
||||
list: [],
|
||||
Lindex: -1,
|
||||
isshow: false,
|
||||
type:0,
|
||||
show:false,
|
||||
Index:''
|
||||
};
|
||||
},
|
||||
onShow() {
|
||||
this.getDate()
|
||||
},
|
||||
onLoad({type}){
|
||||
if(type){
|
||||
this.type=type
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//跳转到地址编辑页面
|
||||
editAddress(item){
|
||||
this.$u.route('/pageA/new_address/index?info=' +JSON.stringify(item));
|
||||
},
|
||||
async getDate() {
|
||||
try{
|
||||
let res = await this.$api.get('/v1/shipping-addresses', {}, {
|
||||
custom: {
|
||||
loading: true
|
||||
},
|
||||
});
|
||||
this.list = res
|
||||
}catch(err){}finally{
|
||||
this.isFirstLoading=false
|
||||
}
|
||||
},
|
||||
//长按事件
|
||||
long_press(index) {
|
||||
this.Lindex = index
|
||||
},
|
||||
//设置为默认
|
||||
async set_default(item) {
|
||||
let obj = {
|
||||
zone_id: item.zone_id,
|
||||
consignee: item.name ? item.name : '11111',
|
||||
telephone: item.telephone,
|
||||
address: item.address,
|
||||
is_default: true
|
||||
}
|
||||
let res = await this.$api.put(`/v1/shipping-addresses/${item.id}`, obj, {
|
||||
custom: {
|
||||
loading: true
|
||||
},
|
||||
});
|
||||
this.Lindex = -1
|
||||
this.getDate()
|
||||
},
|
||||
//复制
|
||||
copy(item) {
|
||||
uniCopy({
|
||||
content:item.address ?? '',
|
||||
success:(res)=>{
|
||||
this.$u.toast(res)
|
||||
},
|
||||
error:(e)=>{
|
||||
this.$u.toast(e)
|
||||
}
|
||||
})
|
||||
this.Lindex = -1
|
||||
},
|
||||
//删除
|
||||
Del(index) {
|
||||
this.show=true
|
||||
this.Index=index
|
||||
},
|
||||
async confirm(){
|
||||
try{
|
||||
this.$api.delete(`/v1/shipping-addresses/${this.list[this.Index].id}`, {}, {
|
||||
custom: {
|
||||
loading: true
|
||||
},
|
||||
});
|
||||
this.list.splice(this.Index, 1)
|
||||
}catch(err){}
|
||||
},
|
||||
//选择地址
|
||||
Select(item) {
|
||||
if(this.type!=1){
|
||||
uni.$emit('address:select', item)
|
||||
uni.navigateBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #FFFFFF;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.Border{
|
||||
border-bottom: 1rpx solid #E5E5E5;
|
||||
}
|
||||
.btn {
|
||||
@apply mr-66rpx flex items-center justify-center w-98rpx h-98rpx rounded-full
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<view>
|
||||
<view class="fixed left-0 right-0 w-full z-50">
|
||||
<u-tabs active-color="#378264" :is-scroll="false" height="80" bar-width="110" bar-height="8" :list="tabsList"
|
||||
:current="tabsCurrent" @change="tabChange"></u-tabs>
|
||||
</view>
|
||||
<mescroll-body top="90" :height="height" ref="mescrollRef" @init="mescrollInit" @down="downCallback"
|
||||
@up="upCallback" :down="downOption" :up="upOption">
|
||||
<view v-for="(item,index) in dataList" :key="index"
|
||||
class="w-710rpx bg-white rounded-xs m-auto mt-base px-base pb-base">
|
||||
<view class="py-base border-b text-txBase text-lg border-txBorder">订单编号:{{item.order_sn}}</view>
|
||||
<view class="flex mt-46rpx ">
|
||||
<u-image border-radius="10" width="190rpx" height="190rpx" :src="item.product.cover" :lazy-load="true">
|
||||
</u-image>
|
||||
<view class="ml-10rpx flex-1 ">
|
||||
<view class="text-txBase text-lg two-ellipsis h-80rpx">{{item.product.name}}</view>
|
||||
<view class="flex justify-end">
|
||||
<view @tap="$u.route('/pageA/after_sales_detail/index',{id:item.id})"
|
||||
class=" mt-30rpx w-180rpx h-66rpx bg-primary text-center leading-66rpx rounded-lg text-white text-lg">
|
||||
查看
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="item.remarks" class="mt-55rpx p-base bg-hex-F2F0F0 min-h-108rpx rounded-xs text-md text-txGray">
|
||||
{{item.remarks}}
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
export default {
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
tabsList: [{
|
||||
name: '处理中',
|
||||
doing: true
|
||||
}, {
|
||||
name: '申请记录',
|
||||
doing: ''
|
||||
}],
|
||||
tabsCurrent: 0,
|
||||
downOption: {
|
||||
auto: false,
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20
|
||||
},
|
||||
noMoreSize: 1
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
height() {
|
||||
const {
|
||||
windowHeight,
|
||||
statusBarHeight
|
||||
} = this.$u.sys();
|
||||
return windowHeight - statusBarHeight - 44 + 'px';
|
||||
},
|
||||
},
|
||||
onLoad() {
|
||||
uni.$on('refresh', () => {
|
||||
this.downCallback()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
tabChange(index) {
|
||||
this.tabsCurrent = index;
|
||||
this.downCallback()
|
||||
},
|
||||
downCallback() {
|
||||
this.dataList = []
|
||||
this.mescroll.resetUpScroll();
|
||||
},
|
||||
async upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData(page) {
|
||||
let obj = {
|
||||
page: page.num,
|
||||
per_page: page.size,
|
||||
doing: this.tabsList[this.tabsCurrent].doing,
|
||||
}
|
||||
this.$api.get(`/v1/after-sales`, {
|
||||
params: obj
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.length)
|
||||
if (page.num == 1) this.dataList = [];
|
||||
this.dataList = this.dataList.concat(res.data);
|
||||
}).catch(err => {
|
||||
this.mescroll.endErr()
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,439 @@
|
|||
<template>
|
||||
<view>
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<view class="h-420rpx p-base bottom-bg">
|
||||
<view class="text-lg font-extrabold text-white text-left">{{detail.state|saleText}}</view>
|
||||
<view class="py-30rpx -mx-base mt-40rpx">
|
||||
<block v-if="detail.state!=7">
|
||||
<cu-steps mode="icon" icon="checkmark-circle-fill" active-color="#ffffff" active-text-color="#fff"
|
||||
un-active-text-color="#fff" un-active-color="#A6A6A6" :list="numList" :current="current" :text-style="{
|
||||
marginTop: '30rpx',
|
||||
color: '#fff',
|
||||
}"></cu-steps>
|
||||
</block>
|
||||
<block v-else>
|
||||
<cu-steps mode="icon" icon="checkmark-circle-fill" active-color="#ffffff" active-text-color="#fff"
|
||||
un-active-text-color="#fff" un-active-color="#A6A6A6" :list="numLists" :current="current" :text-style="{
|
||||
marginTop: '30rpx',
|
||||
color: '#fff',
|
||||
}"></cu-steps>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<view class="-mt-120rpx px-base relative">
|
||||
<view class="bg-white rounded-xs card h-170rpx pl-24rpx py-18rpx flex"
|
||||
@tap="$u.route('/pageA/after_sales_logistics/index',{id:detail.id})">
|
||||
<view class="flex-1 w-0">
|
||||
<view class="text-lg text-black">{{detail.state|saleText}}</view>
|
||||
<view class="text-lg text-hex-808080 mt-25rpx line-1">{{detail.remarks?detail.remarks:'资料已提交,请耐心等待客服审核'}}</view>
|
||||
</view>
|
||||
<view class="flex items-center pr-20rpx text-hex-808080">
|
||||
<u-icon name="arrow-right" size="30"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white rounded-xs mt-30rpx p-base">
|
||||
<view class="text-lg text-black">{{detail.type|typeList}} </view>
|
||||
<view class="mt-36rpx">
|
||||
<view class="text-hex-808080">问题描述:{{detail.description}}</view>
|
||||
<view class="grid grid-cols-3 gap-x-45rpx gap-y-23rpx mt-14rpx">
|
||||
<u-image v-for="(item,index) in detail.images" :key="index" class="flex-none" border-radius="8rpx"
|
||||
width="190" height="190" :src="item" :lazy-load="true"></u-image>
|
||||
</view>
|
||||
<block >
|
||||
<view class="flex items-center justify-between mt-30rpx">
|
||||
<view>回复:</view>
|
||||
<block v-if="detail.type==1||detail.type==2">
|
||||
<view v-if="detail.state==3 || detail.state==4 ||detail.state==5" class="text-hex-d43030">退款金额:{{detail.amount}}元</view>
|
||||
</block>
|
||||
</view>
|
||||
<view class="mt-base">{{detail.remarks?detail.remarks:'资料已提交,请耐心等待客服审核'}}</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white rounded-xs mt-30rpx p-base">
|
||||
<view class="text-lg text-black">商品信息</view>
|
||||
<view class="mt-16rpx flex">
|
||||
<u-image class="flex-none" border-radius="15rpx" width="190" height="190" :src="detail.product.cover"
|
||||
:lazy-load="true"></u-image>
|
||||
<view class="ml-10rpx flex-1">
|
||||
<view class="text-hex-808080 text-lg h-90rpx">{{detail.product.name}}</view>
|
||||
<view class="flex justify-between mt-6rpx">
|
||||
<view class="text-black text-lg flex-1"> 单价:¥{{detail.product.sell_price}} </view>
|
||||
<view class="text-black text-lg flex-1"> 数量:{{detail.product.num}} </view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="bg-white rounded-xs mt-30rpx p-base"> -->
|
||||
<!-- 货运单号 -->
|
||||
<block v-if="(detail.type==1 ||detail.type==3)&&detail.state==3 ">
|
||||
<view class="flex items-center mt-40rpx ">
|
||||
<view>退货运单号:</view>
|
||||
<u-input class="bg-white" border placeholder="请输入运单号" v-model="textContent" />
|
||||
</view>
|
||||
</block>
|
||||
<!-- 打回重填 -->
|
||||
<block v-if="detail.state==1">
|
||||
<view class="bg-white rounded-xs mt-30rpx p-base">
|
||||
<!-- <view class="bg-white rounded-xs "> -->
|
||||
<view class="text-lg text-black flex items-center " @tap="typeShow = true">
|
||||
<view class="w-120rpx">
|
||||
<text class="imt">类型:</text>
|
||||
</view>
|
||||
<view class="flex-1 text-right text-hex-808080">
|
||||
<text class="mr-10rpx">{{ typeText }}</text>
|
||||
<u-icon name="arrow-right"></u-icon>
|
||||
</view>
|
||||
<!-- </view> -->
|
||||
</view>
|
||||
<view class="text-lg text-black mt-10rpx imt">问题描述 </view>
|
||||
<view class="mt-10rpx">
|
||||
<textarea class="min-h-130rpx w-full" placeholder-style="text-h999" placeholder="为了更好的解决您的问题,请尽量描述详细"
|
||||
maxlength="100" auto-height v-model="content"></textarea>
|
||||
|
||||
<view class="py-12rpx grid grid-cols-3 gap-20rpx">
|
||||
<view class="relative w-full" style="padding-top: 100%;" v-for="(img, index) in tempFilePaths"
|
||||
:key="index">
|
||||
<view class="img absolute left-0 top-0 w-full h-full">
|
||||
<image class="img1 w-full h-full" :src="img"></image>
|
||||
<view
|
||||
class="absolute bg-hex-f30606 rounded-full w-48rpx h-48rpx flex items-center justify-center -top-10rpx -right-10rpx text-gray"
|
||||
@tap="onDeleteImg(index)">
|
||||
<u-icon size="20" color="#ffffff" name="close"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="relative w-full" style="padding-top: 100%;" v-if="tempFilePaths.length < maxImgLength">
|
||||
<view class="img absolute left-0 top-0 w-full h-full" @tap="openImage">
|
||||
<u-icon name="camera"></u-icon>
|
||||
<text class="text-28rpx mt-6rpx">图片</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 字数统计 -->
|
||||
<!-- <view class="text-h999 absolute right-18rpx bottom-30rpx">{{ content.length }}/100</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<view>
|
||||
<view class="py-35rpx px-base">
|
||||
<view class="login-btn " v-if="detail.state==1" @tap="onSubmit"> 补充资料 </view>
|
||||
<view class="login-btn " v-if="detail.state==3" @tap="onConfirm"> 确认 </view>
|
||||
<view class="login-btn mt-base" v-if="detail.state==1 ||detail.state==2 ||detail.state==3" @tap="cancelShow=true">
|
||||
取消申请 </view>
|
||||
</view>
|
||||
<!-- </view> -->
|
||||
</view>
|
||||
<u-select v-model="typeShow" @confirm="onChangeType" :list="typeList" :default-value="type"></u-select>
|
||||
<u-action-sheet :list="list" @tap="uploadImge" v-model="show"></u-action-sheet>
|
||||
<cu-modal v-model="cancelShow" @confirm="confirm" confirm-color="#378264" show-cancel-button
|
||||
content="是否取消申请"></cu-modal>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
album,
|
||||
camera
|
||||
} from '@/utils/author.js'
|
||||
import uploadImage from '@/utils/ossutil/uploadFile.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isFirstLoading:true,
|
||||
id:'',
|
||||
show: false,
|
||||
list: [{
|
||||
text: '拍摄',
|
||||
fontSize: 28,
|
||||
color: '#383838'
|
||||
},
|
||||
{
|
||||
text: '从相册选择',
|
||||
fontSize: 28,
|
||||
color: '#383838'
|
||||
}
|
||||
],
|
||||
detail: {
|
||||
product: {}
|
||||
},
|
||||
textContent: '', //退货运单号
|
||||
tempFilePaths: [], //
|
||||
imgList: [],
|
||||
maxImgLength: 6,
|
||||
content: '',
|
||||
numList: [{
|
||||
name: '提交申请',
|
||||
},
|
||||
{
|
||||
name: '客服审核',
|
||||
},
|
||||
{
|
||||
name: '客户确认',
|
||||
},
|
||||
{
|
||||
name: '完成',
|
||||
},
|
||||
],
|
||||
numLists: [{
|
||||
name: '提交申请',
|
||||
},
|
||||
{
|
||||
name: '取消申请',
|
||||
},
|
||||
{
|
||||
name: '完成',
|
||||
},
|
||||
],
|
||||
typeShow: false,
|
||||
type: [],
|
||||
content: '',
|
||||
cancelShow:false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
current() {
|
||||
return this.detail.state - 2
|
||||
},
|
||||
typeText() {
|
||||
return this.typeList[this.type[0]]?.label ?? '请选择';
|
||||
},
|
||||
typeList(){
|
||||
const price=Number(this.detail.product.total_amount)
|
||||
const isprice=price>0?true:false
|
||||
let arr=[
|
||||
{
|
||||
value: 1,
|
||||
label: '退款退货',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '退款',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '换货',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '漏发',
|
||||
},
|
||||
]
|
||||
if(this.detail.product.is_gift){
|
||||
arr=[
|
||||
{
|
||||
value: 3,
|
||||
label: '换货',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '漏发',
|
||||
},
|
||||
]
|
||||
}else if(!this.detail.product.is_gift && !isprice){
|
||||
arr=[
|
||||
{
|
||||
value: 1,
|
||||
label: '退款退货',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '换货',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '漏发',
|
||||
},
|
||||
]
|
||||
}
|
||||
return arr
|
||||
}
|
||||
},
|
||||
onLoad({
|
||||
id
|
||||
}) {
|
||||
this.id=id
|
||||
this.getDate(id)
|
||||
},
|
||||
methods: {
|
||||
//获取详情
|
||||
async getDate(id) {
|
||||
try{
|
||||
let resDate = await this.$api.get(`/v1/after-sales/${id}`)
|
||||
this.detail = resDate
|
||||
}catch(err){}finally{
|
||||
this.isFirstLoading=false
|
||||
}
|
||||
},
|
||||
//类型改变
|
||||
onChangeType(val) {
|
||||
this.type = [this.typeList.findIndex((e) => e.value == val[0].value)];
|
||||
},
|
||||
onDeleteImg(index) {
|
||||
this.tempFilePaths.splice(index, 1);
|
||||
},
|
||||
//上传的图片
|
||||
async uploadImge(index) {
|
||||
//相机
|
||||
if (index == 0) {
|
||||
// #ifdef APP-PLUS
|
||||
// let result = await camera()
|
||||
// if (!result) return
|
||||
// #endif
|
||||
this.openImage('camera')
|
||||
} else {
|
||||
//相册
|
||||
// #ifdef APP-PLUS
|
||||
// let result = await album()
|
||||
// if (!result) return
|
||||
// #endif
|
||||
this.openImage('album')
|
||||
}
|
||||
},
|
||||
openImage(val) {
|
||||
uni.chooseImage({
|
||||
count: this.maxImgLength-this.tempFilePaths.length,
|
||||
// sourceType: [val],
|
||||
sizeType: ['compressed'],
|
||||
success: async (res) => {
|
||||
res.tempFilePaths.forEach(item => {
|
||||
this.tempFilePaths.push(item)
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
},
|
||||
//上传
|
||||
async uploads() {
|
||||
const resData = await this.$api.post('/v1/oss-sts');
|
||||
// const host = `https://${resData.bucket}.${resData.region_id}.aliyuncs.com/`
|
||||
const host = `https://${resData.domain}/`;
|
||||
const arr = []
|
||||
try {
|
||||
for (let i = 0; i < this.tempFilePaths.length; i++) {
|
||||
const upFile = new Promise((r) => {
|
||||
uploadImage(
|
||||
this.tempFilePaths[i], {
|
||||
uploadImageUrl: host,
|
||||
AccessKeySecret: resData.AccessKeySecret,
|
||||
OSSAccessKeyId: resData.AccessKeyId,
|
||||
stsToken: resData.SecurityToken,
|
||||
timeout: 3600,
|
||||
dir: 'app/order/',
|
||||
},
|
||||
(result) => {
|
||||
this.imgList.push(result)
|
||||
r()
|
||||
},
|
||||
(err) => {
|
||||
console.log(err);
|
||||
},
|
||||
);
|
||||
})
|
||||
arr.push(upFile)
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
this.$u.toast(err.message || err.errMsg);
|
||||
}
|
||||
return Promise.all(arr)
|
||||
},
|
||||
onUpload() {
|
||||
const canCount = this.maxImgLength - this.tempFilePaths.length;
|
||||
if (canCount <= 0) {
|
||||
return this.$u.toast('最多上传6张!');
|
||||
}
|
||||
this.imgList=[]
|
||||
this.show = true
|
||||
},
|
||||
//补充资料
|
||||
async onSubmit() {
|
||||
if (this.typeList[this.type[0]]?.value == null || this.typeList[this.type[0]]?.value == '') return this.$u.toast('请选择类型');
|
||||
if (this.content == null || this.content == '') return this.$u.toast('请填写描述');
|
||||
if (!this.tempFilePaths.length) return this.$u.toast('请上传相关资料');
|
||||
await this.uploads()
|
||||
let obj = {
|
||||
type: this.typeList[this.type[0]]?.value,
|
||||
description: this.content,
|
||||
images: this.imgList
|
||||
}
|
||||
// if (obj.type == null || obj.type == '') return this.$u.toast('请选择类型');
|
||||
// if (obj.description == null || obj.description == '') return this.$u.toast('请填写描述');
|
||||
// if (!obj.images.length) return this.$u.toast('请上传相关资料');
|
||||
let resDate = await this.$api.put(`/v1/after-sales/${this.detail.id}`,obj)
|
||||
this.$u.toast('提交成功')
|
||||
this.getDate(this.id)
|
||||
},
|
||||
//确认收货
|
||||
async onConfirm() {
|
||||
let obj = {}
|
||||
if ((this.detail.type == 1 || this.detail.type == 3) && !this.textContent) {
|
||||
return this.$u.toast('请输入运单号,并用逗号隔开')
|
||||
}
|
||||
if (this.detail.type == 1 || this.detail.type == 3) {
|
||||
obj.tracking_number = this.textContent
|
||||
}
|
||||
let resDate = await this.$api.put(`/v1/after-sales/${this.detail.id}/agree`, obj)
|
||||
this.getDate(this.id)
|
||||
},
|
||||
//取消申请
|
||||
async confirm(){
|
||||
try{
|
||||
await this.$api.delete(`/v1/after-sales/${this.detail.id}`, {}, {
|
||||
custom: {
|
||||
loading: true
|
||||
},
|
||||
});
|
||||
this.$u.toast("取消成功")
|
||||
uni.navigateBack()
|
||||
uni.$emit('refresh')
|
||||
}catch(err){}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.bottom-bg {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
background: $u-type-primary;
|
||||
clip-path: ellipse(120% 100% at 50% 0%);
|
||||
}
|
||||
|
||||
.clipPath {
|
||||
clip-path: ellipse(120% 40% at 50% 0%) !important;
|
||||
}
|
||||
|
||||
.imt {
|
||||
position: relative;
|
||||
padding-left: 20rpx;
|
||||
|
||||
&::before {
|
||||
content: '*';
|
||||
color: #d43030;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: #999;
|
||||
border-radius: 8rpx;
|
||||
background-color: #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
<template>
|
||||
<view class="p-40rpx">
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<u-time-line>
|
||||
<u-time-line-item v-for="(item,index) in dataList" :key="index">
|
||||
<template v-slot:node >
|
||||
<image v-if="index==0" class="w-48rpx h-48rpx" src="/static/images/logistics/complete.png" mode=""></image>
|
||||
<view v-else class="u-node"></view>
|
||||
</template>
|
||||
<template v-slot:content>
|
||||
<view>
|
||||
<view class="u-order-title">{{item.name}}</view>
|
||||
<view class="u-order-desc">{{item.desc}}</view>
|
||||
<view class="grid grid-cols-3 gap-20rpx my-base" >
|
||||
<view v-for="(ite,inde) in item.images" :key="inde" class="relative w-full" style="padding-top: 100%;">
|
||||
<view class="img absolute left-0 top-0 w-full h-full">
|
||||
<image class=" w-full h-full" :src="ite" mode=""></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-order-time">{{item.created_at}}</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-time-line-item>
|
||||
</u-time-line>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isFirstLoading:true,
|
||||
dataList: [],
|
||||
};
|
||||
},
|
||||
onLoad({
|
||||
id
|
||||
}) {
|
||||
this.getDate(id);
|
||||
},
|
||||
methods: {
|
||||
async getDate(id) {
|
||||
try{
|
||||
let resDate = await this.$api.get(`/v1/after-sales/${id}/logs`);
|
||||
this.dataList = resDate;
|
||||
}catch(err){}finally{
|
||||
this.isFirstLoading=false
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.u-node {
|
||||
width: 15rpx;
|
||||
height: 15rpx;
|
||||
border-radius: 100rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #d0d0d0;
|
||||
}
|
||||
|
||||
.u-order-title {
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.u-order-desc {
|
||||
color: rgb(150, 150, 150);
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.u-order-time {
|
||||
color: rgb(200, 200, 200);
|
||||
font-size: 26rpx;
|
||||
}
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
<template>
|
||||
<view class="">
|
||||
<!-- 导航栏 -->
|
||||
<u-navbar back-icon-color="#000000" title-color="#000000" :border-bottom="false">
|
||||
<view class="flex-1 text-center text-32rpx">余额转账</view>
|
||||
<view slot="right" class="pr-base text-28rpx text-primary" @tap="$u.routeAuth('/pageA/balance_transfer_details/index')"> 转账明细 </view>
|
||||
</u-navbar>
|
||||
<nav-shadow></nav-shadow>
|
||||
<view class="px-60rpx pt-100rpx">
|
||||
<view class="flex items-center text-txBase text-lg mt-60rpx">
|
||||
<view class="w-120rpx">手机号</view>
|
||||
<input
|
||||
:adjust-position="false"
|
||||
placeholder-class="text-txGray"
|
||||
class="inputHeight rounded-lg"
|
||||
v-model="phone"
|
||||
type="number"
|
||||
placeholder="请输入手机号"
|
||||
/>
|
||||
</view>
|
||||
<view class="flex items-center text-txBase text-lg mt-60rpx">
|
||||
<view class="w-120rpx">金额</view>
|
||||
<input
|
||||
:adjust-position="false"
|
||||
placeholder-class="text-txGray"
|
||||
class="inputHeight rounded-lg"
|
||||
v-model="amount"
|
||||
type="number"
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</view>
|
||||
<view class="text-right text-txBase text-lg mt-60rpx">可转余额:{{ balance.balance }}</view>
|
||||
<view class="text-center text-txGray text-md mt-60rpx">提示:请认真填写,避免发生错误!</view>
|
||||
<view class="mt-50rpx">
|
||||
<view class="login-btn" @tap="onSubmit"> 确认转账 </view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 判断是否设置了安全密码 -->
|
||||
<cu-modal
|
||||
v-model="tipsShow"
|
||||
title="您还未设置支付密码?"
|
||||
:showCancelButton="true"
|
||||
confirmText="去设置"
|
||||
@confirm="$u.routeAuth('/pageA/reset_password/secure')"
|
||||
content="提示:请先设置后在进行操作~"
|
||||
></cu-modal>
|
||||
<!-- 输入密码 -->
|
||||
<password-popup v-model="inputShow" @getPassword="confirm">
|
||||
<view class="text-md text-txBase text-center">向【{{ phone }}】转出余额:{{ (amount * 1).toFixed(2) }}</view>
|
||||
<view v-if="passwordError" class="text-txBase text-center text-error">密码错误</view>
|
||||
</password-popup>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { mcl } from '@/utils/index.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tipsShow: false,
|
||||
inputShow: false,
|
||||
passwordError: false,
|
||||
content: '你还未设置安全密码,是否立即前往设置?',
|
||||
phone: '',
|
||||
amount: '',
|
||||
min: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.getters.user ?? {};
|
||||
},
|
||||
balance() {
|
||||
return this.$store.getters.user?.balance ?? {};
|
||||
},
|
||||
wallet() {
|
||||
return this.$store.getters.user?.wallet ?? {};
|
||||
},
|
||||
//判断提现的金额是否大于余额
|
||||
judge() {
|
||||
return this.amount.trim() * 1 - this.balance.balance;
|
||||
},
|
||||
//判断最低金额
|
||||
minamount() {
|
||||
return this.amount.trim() * 1 - this.min * 1;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
if (!this.phone) {
|
||||
return this.$u.toast('手机号不能为空');
|
||||
}
|
||||
if (!this.$u.test.mobile(this.phone)) {
|
||||
return this.$u.toast('手机号格式不正确');
|
||||
}
|
||||
if(this.phone==this.user.phone){
|
||||
return this.$u.toast('不能给自己转账')
|
||||
}
|
||||
if (this.minamount <= 0) {
|
||||
this.$u.toast(`转账的金额不能为0`);
|
||||
} else if (this.judge > 0) {
|
||||
this.$u.toast(`最高转账金额为${this.balance.balance}元`);
|
||||
} else {
|
||||
!this.wallet.has_password ? (this.tipsShow = true) : (this.inputShow = true);
|
||||
}
|
||||
},
|
||||
async confirm(e) {
|
||||
this.inputShow = false;
|
||||
try {
|
||||
let obj = {
|
||||
phone: this.phone,
|
||||
amount: mcl(this.amount, 100),
|
||||
wallet_password: e,
|
||||
};
|
||||
await this.$api.post('/v1/wallet/balance-transfer', obj, {
|
||||
custom: {
|
||||
loading: true,
|
||||
toast: false,
|
||||
},
|
||||
});
|
||||
this.amount = '';
|
||||
this.$store.dispatch('user/getUserInfo');
|
||||
this.$u.toast('转账成功');
|
||||
uni.redirectTo({
|
||||
url: '/pageA/balance_transfer_details/index',
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.errcode == 10001) {
|
||||
this.passwordError = true;
|
||||
this.inputShow = true;
|
||||
} else {
|
||||
this.$u.toast(err.message ?? '系统繁忙,请稍后再试');
|
||||
this.inputShow = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.inputHeight {
|
||||
border: 1rpx solid;
|
||||
@apply h-80rpx flex-1 border border-txBorder px-base ml-40rpx text-lg text-txBase;
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<template>
|
||||
<view>
|
||||
<nav-shadow></nav-shadow>
|
||||
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
|
||||
:up="upOption">
|
||||
<view class="flex items-center text-lg text-txBase text-center py-30rpx ">
|
||||
<view class="flex-1 ">来源/支出</view>
|
||||
<view class="flex-1">时间</view>
|
||||
<view class="flex-1">金额</view>
|
||||
</view>
|
||||
<view @tap="showLogs(item.remarks)" v-for="(item,index) in dataList" :key="index" class="flex items-center text-lg text-txGray text-center pt-base">
|
||||
<view class="flex-1 text-center flex justify-center ">
|
||||
<view class="one-ellipsis w-150rpx">
|
||||
{{item.remarks}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-1 text-center">{{item.created_at}}</view>
|
||||
<view class="flex-1 text-center">{{item.change_balance}}</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
<!-- 弹窗 -->
|
||||
<cu-modal v-model="show" title="详情" :content="logs" mask-close-able></cu-modal>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
export default {
|
||||
mixins: [MescrollMixin], // 使用mixin
|
||||
data() {
|
||||
return {
|
||||
show:false,
|
||||
downOption: {
|
||||
auto: false,
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20
|
||||
},
|
||||
noMoreSize: 1
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
logs:''
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
showLogs(val){
|
||||
this.show=true
|
||||
this.logs=val
|
||||
},
|
||||
downCallback() {
|
||||
this.mescroll.resetUpScroll();
|
||||
this.dataList = []
|
||||
},
|
||||
async upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData(page) {
|
||||
this.$api.get(`/v1/wallet/balance-logs`, {
|
||||
params: {
|
||||
page: page.num,
|
||||
per_page: page.size,
|
||||
action:'transfer'
|
||||
}
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.length)
|
||||
if (page.num == 1) this.dataList = [];
|
||||
this.dataList = this.dataList.concat(res.data);
|
||||
}).catch(err => {
|
||||
this.mescroll.endErr()
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
min-height: 100vh;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
<template>
|
||||
<view class="px-base">
|
||||
<u-navbar back-icon-color="#000000" title-color="#000000" :border-bottom="false" >
|
||||
<view class="flex-1 text-center text-32rpx">提现到银行卡</view>
|
||||
<view slot="right" class="pr-base text-28rpx text-primary" @tap="$u.routeAuth('/pageA/bank_details/index')"> 提现记录 </view>
|
||||
</u-navbar>
|
||||
<view class="u-skeleton">
|
||||
<block v-if="isFirstLoading">
|
||||
<view class="w-750rpx h-750rpx u-skeleton-rect"></view>
|
||||
<view class="px-24rpx">
|
||||
<view class="w-600rpx h-44rpx u-skeleton-rect mt-20rpx"></view><view class="w-333rpx h-36rpx u-skeleton-rect mt-20rpx"></view>
|
||||
<view class="flex justify-between items-center"> <view class="w-142rpx h-44rpx u-skeleton-rect mt-20rpx"></view></view>
|
||||
</view>
|
||||
</block>
|
||||
<block>
|
||||
<view class="pb-30rpx bg-white box-shadow mt-50rpx">
|
||||
<!-- 有银行信息 -->
|
||||
<block v-if="userBank.bank_number">
|
||||
<view class="flex items-center px-48rpx pt-30rpx text-lg">
|
||||
<view class="text-txBase w-140rpx">银行卡号</view>
|
||||
<view class="text-txGray ml-50rpx">{{ userBank.bank_number }}</view>
|
||||
</view>
|
||||
<view class="flex items-center px-48rpx pt-30rpx text-lg">
|
||||
<view class="text-txBase w-140rpx">银行</view>
|
||||
<view class="text-txGray ml-50rpx">{{ userBank.bank_name }}</view>
|
||||
</view>
|
||||
<view class="border-b border-txBorder pb-30rpx flex items-center px-48rpx pt-30rpx text-lg">
|
||||
<view class="text-txBase w-140rpx">持卡人</view>
|
||||
<view class="text-txGray ml-50rpx">{{ userBank.real_name }}</view>
|
||||
</view>
|
||||
<view @tap="$u.routeAuth('/pageA/my_bank/index')" class="text-right text-hex-2979ff text-lg mt-30rpx px-base underline"
|
||||
>修改银行卡信息</view
|
||||
>
|
||||
</block>
|
||||
<block v-else>
|
||||
<view @tap="$u.routeAuth('/pageA/my_bank/index')" class="text-center py-80rpx text-lg underline">暂无银行信息,请先去完善 </view>
|
||||
</block>
|
||||
<view class="flex items-center text-txBase text-lg mt-60rpx px-48rpx">
|
||||
<view class="w-120rpx">金额</view>
|
||||
<input
|
||||
:adjust-position="false"
|
||||
placeholder-class="text-txGray"
|
||||
class="inputHeight rounded-lg"
|
||||
v-model="amount"
|
||||
type="number"
|
||||
:placeholder="`可提余额:${wallet.balance}`"
|
||||
/>
|
||||
</view>
|
||||
<block v-if="userBank.bank_number">
|
||||
<view class="flex items-center justify-between text-txGray text-lg mt-base px-48rpx">
|
||||
<view>到账{{ calculation }}</view>
|
||||
<view>手续费{{ sx }}元</view>
|
||||
</view>
|
||||
</block>
|
||||
<view class="text-hex-909399 text-20rpx mt-base px-48rpx">{{ withdraw.threshold_amount }}元起提,提现手续费为{{ sxf }}</view>
|
||||
<!-- <view class="text-right text-txBase text-lg mt-30rpx px-48rpx">可转余额:{{wallet.balance}}</view> -->
|
||||
</view>
|
||||
<view class="text-center text-hex-D43030 text-md mt-60rpx">提示:每周一开始对上周提现依次打款 !</view>
|
||||
<!-- 有银行卡信息 -->
|
||||
<view v-if="userBank.bank_number" class="mt-50rpx px-20rpx">
|
||||
<view class="login-btn" @tap="onSubmit"> 确认提现 </view>
|
||||
</view>
|
||||
<!-- 没有银行卡信息 -->
|
||||
<view v-else class="mt-50rpx px-20rpx">
|
||||
<view class="login-btn" @tap="tips"> 确认提现 </view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<!-- 判断是否设置了安全密码 -->
|
||||
<cu-modal
|
||||
v-model="tipsShow"
|
||||
title="您还未设置支付密码?"
|
||||
:showCancelButton="true"
|
||||
confirmText="去设置"
|
||||
@confirm="$u.routeAuth('/pageA/reset_password/secure')"
|
||||
content="提示:请先设置后在进行操作~"
|
||||
></cu-modal>
|
||||
<!-- 账号输入密码确认 -->
|
||||
<password-popup v-model="inputShow" @getPassword="confirm">
|
||||
<view class="text-md text-txBase text-center">转出余额:{{ (amount * 1).toFixed(2) }}</view>
|
||||
<view v-if="passwordError" class="text-txBase text-center text-error">密码错误</view>
|
||||
</password-popup>
|
||||
<u-skeleton :loading="isFirstLoading" el-color="#f5f5f5" :animation="true" bgColor="#f5f5f5"></u-skeleton>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mcl } from '@/utils/index.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isFirstLoading: true,
|
||||
userBank: {},
|
||||
tipsShow: false,
|
||||
inputShow: false,
|
||||
passwordError: false,
|
||||
content: '你还未设置安全密码,是否立即前往设置?',
|
||||
amount: '',
|
||||
wallet_password: '',
|
||||
};
|
||||
},
|
||||
onShow() {
|
||||
this.getDate();
|
||||
},
|
||||
computed: {
|
||||
withdraw() {
|
||||
return this.$store.getters.config?.withdraw ?? {};
|
||||
},
|
||||
wallet() {
|
||||
return this.$store.getters.user?.wallet ?? {};
|
||||
},
|
||||
//判断提现的金额是否大于余额
|
||||
judge() {
|
||||
return this.amount.trim() * 1 - this.wallet.balance;
|
||||
},
|
||||
//判断最低金额
|
||||
minamount() {
|
||||
return this.amount.trim() * 1 - this.withdraw.threshold_amount * 1;
|
||||
},
|
||||
//处理手续费%
|
||||
sxf() {
|
||||
let str = Number(this.withdraw.rate )
|
||||
str += '%';
|
||||
return str;
|
||||
},
|
||||
//手续费
|
||||
sx() {
|
||||
return (this.amount.trim() * 1 * this.withdraw.rate/100).toFixed(2);
|
||||
},
|
||||
//到账
|
||||
calculation() {
|
||||
const str = (this.amount * (1 - this.withdraw.rate * 1/100)).toFixed(2);
|
||||
return str ?? '0.00';
|
||||
},
|
||||
},
|
||||
onLoad() {
|
||||
console.log(this.withdraw.rate)
|
||||
},
|
||||
methods: {
|
||||
async getDate() {
|
||||
try {
|
||||
let resDate = await this.$api.get('/v1/user-bank');
|
||||
this.userBank = resDate;
|
||||
} catch (err) {
|
||||
} finally {
|
||||
this.isFirstLoading = false;
|
||||
}
|
||||
},
|
||||
//提交
|
||||
onSubmit() {
|
||||
if (this.minamount < 0) {
|
||||
this.$u.toast(`最低提现${this.withdraw.threshold_amount}元`);
|
||||
} else if (this.judge > 0) {
|
||||
this.$u.toast(`最高提现金额为${this.wallet.balance}元`);
|
||||
} else {
|
||||
!this.wallet.has_password ? (this.tipsShow = true) : (this.inputShow = true);
|
||||
}
|
||||
},
|
||||
tips() {
|
||||
this.$u.toast('请先去完善银行卡信息');
|
||||
},
|
||||
async confirm(e) {
|
||||
this.inputShow = false;
|
||||
try {
|
||||
let obj = {
|
||||
amount: mcl(this.amount, 100),
|
||||
wallet_password: e,
|
||||
};
|
||||
await this.$api.post('/v1/wallet/wallet-to-bank', obj, {
|
||||
custom: {
|
||||
loading: true,
|
||||
toast: false,
|
||||
},
|
||||
});
|
||||
this.amount = '';
|
||||
this.$store.dispatch('user/getUserInfo');
|
||||
this.$u.toast('提现成功');
|
||||
uni.redirectTo({
|
||||
url: '/pageA/bank_details/index',
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.errcode == 10001) {
|
||||
this.passwordError = true;
|
||||
this.inputShow = true;
|
||||
} else {
|
||||
this.$u.toast(err.message ?? '系统繁忙,请稍后再试');
|
||||
this.inputShow = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.box-shadow {
|
||||
box-shadow: 0px 2rpx 10rpx rgba(0, 0, 0, 0.2);
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
|
||||
.inputHeight {
|
||||
@apply h-80rpx flex-1 border border-txBorder px-base ml-40rpx text-lg text-txBase;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
<template>
|
||||
<view>
|
||||
<nav-shadow></nav-shadow>
|
||||
<!-- 申请中 已成功 -->
|
||||
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
|
||||
:up="upOption">
|
||||
<view class="pt-26rpx">
|
||||
<block v-for="(item,index) in dataList" :key="index">
|
||||
<block v-if="item.status!=2">
|
||||
<view class="w-710rpx mb-base box-shadow m-auto px-30rpx py-base text-lg">
|
||||
<view class="flex items-center ">
|
||||
<view class="text-txBase w-120rpx">卡号</view>
|
||||
<view class="flex items-center justify-between flex-1">
|
||||
<view class="text-txGray">{{item.bank_number|hideBankCard}}</view>
|
||||
<view class="text-hex-2979ff px-10rpx bg-hex-F5F8FC rounded-lg" v-if="item.status==0">申请中</view>
|
||||
<view class="text-primary px-10rpx bg-hex-F2F7F5 rounded-lg" v-if="item.status==1">已成功</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex items-center">
|
||||
<view class="text-txBase w-120rpx">银行</view>
|
||||
<view class="text-txGray ">{{item.bank_name}}</view>
|
||||
</view>
|
||||
<view class="flex items-center ">
|
||||
<view class="text-txBase w-120rpx">持卡人</view>
|
||||
<view class="text-txGray flex-1 ">{{item.username}}</view>
|
||||
</view>
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="flex items-center">
|
||||
<view class="text-txBase w-120rpx">金额</view>
|
||||
<view class="text-txGray flex-1 ">{{item.amount}}</view>
|
||||
</view>
|
||||
<view class="text-txBase">手续费:{{item.service_amount}}元</view>
|
||||
</view>
|
||||
<view class="text-right text-md text-txBase mt-10rpx">{{item.created_at}}</view>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 失败 -->
|
||||
<block v-else>
|
||||
<view class="w-710rpx bg-hex-F7F5F5 box-shadow mb-base m-auto px-30rpx py-base text-lg text-txGray">
|
||||
<view class="flex items-center ">
|
||||
<view class=" w-120rpx">卡号</view>
|
||||
<view class="flex items-center justify-between flex-1">
|
||||
<view>{{item.bank_number|hideBankCard}}</view>
|
||||
<view class=" px-10rpx bg-hex-E5E5E5 rounded-lg">已拒绝</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex items-center">
|
||||
<view class="w-120rpx">银行</view>
|
||||
<view>{{item.bank_name}}</view>
|
||||
</view>
|
||||
<view class="flex items-center ">
|
||||
<view class=" w-120rpx">持卡人</view>
|
||||
<view class="flex-1 ">{{item.username}}</view>
|
||||
</view>
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="flex items-center">
|
||||
<view class="text-txBase w-120rpx">金额</view>
|
||||
<view class="text-txGray flex-1 ">{{item.amount}}</view>
|
||||
</view>
|
||||
<view class="text-txBase">手续费:{{item.service_amount}}元</view>
|
||||
</view>
|
||||
<view class="text-right text-md text-txBase">{{item.created_at}}</view>
|
||||
<view class="h-1rpx bg-txBorder mt-base"></view>
|
||||
<view class="mt-base text-hex-D43030">原因:{{item.remarks}}</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
export default {
|
||||
mixins: [MescrollMixin], // 使用mixin
|
||||
data() {
|
||||
return {
|
||||
downOption: {
|
||||
auto: false,
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20
|
||||
},
|
||||
noMoreSize: 1
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
downCallback() {
|
||||
this.mescroll.resetUpScroll();
|
||||
this.dataList = []
|
||||
},
|
||||
async upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData(page) {
|
||||
this.$api.get(`/v1/wallet/wallet-to-bank-logs`, {
|
||||
params: {
|
||||
page: page.num,
|
||||
per_page: page.size
|
||||
}
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.length)
|
||||
if (page.num == 1) this.dataList = [];
|
||||
this.dataList = this.dataList.concat(res.data);
|
||||
}).catch(err => {
|
||||
this.mescroll.endErr()
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
min-height: 100vh;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.box-shadow {
|
||||
box-shadow: 0px 2rpx 10rpx rgba(0, 0, 0, 0.2);
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<template>
|
||||
<view>
|
||||
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
|
||||
:up="upOption">
|
||||
<view class="px-rowSm mt-20rpx">
|
||||
<u-waterfall v-model="dataList" ref="uWaterfall">
|
||||
<template v-slot:left="{ leftList }">
|
||||
<view v-for="(item, index) in leftList" :key="index" class="px-rowSm mb-base">
|
||||
<goods-item :goods="item.sku" :isCollection="true"></goods-item>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:right="{ rightList }">
|
||||
<view v-for="(item, index) in rightList" :key="index" class="px-rowSm mb-base">
|
||||
<goods-item :goods="item.sku" :isCollection="true"></goods-item>
|
||||
</view>
|
||||
</template>
|
||||
</u-waterfall>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
export default {
|
||||
mixins: [MescrollMixin], // 使用mixin
|
||||
data() {
|
||||
return {
|
||||
downOption: {
|
||||
auto: false,
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20
|
||||
},
|
||||
noMoreSize: 1,
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
downCallback() {
|
||||
this.$refs.uWaterfall.clear();
|
||||
this.mescroll.resetUpScroll();
|
||||
},
|
||||
async upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData(page) {
|
||||
this.$api.get(`/v1/product/favorites`, {
|
||||
params: {
|
||||
page: page.num,
|
||||
per_page: page.size
|
||||
}
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.length)
|
||||
if (page.num == 1) this.dataList = [];
|
||||
this.dataList = this.dataList.concat(res.data);
|
||||
}).catch(err => {
|
||||
this.mescroll.endErr()
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<template>
|
||||
<view>
|
||||
<!-- tabs -->
|
||||
<view
|
||||
class="fixed shadow-down left-0 right-0 z-8 bg-white flex items-center justify-between px-57rpx py-rowLg text-lg text-txGray">
|
||||
<view @tap="handType('unuse')" :class="status=='unuse'?'text-primary':''">未使用</view>
|
||||
<view class="w-2rpx h-30rpx bg-txGray"></view>
|
||||
<view @tap="handType('used')" :class="status=='used'?'text-primary':''">已使用</view>
|
||||
<view class="w-2rpx h-30rpx bg-txGray"></view>
|
||||
<view @tap="handType('expired')" :class="status=='expired'?'text-primary':''">已过期</view>
|
||||
</view>
|
||||
<!-- 列表 -->
|
||||
<mescroll-body top="105" :height="height" ref="mescrollRef" @init="mescrollInit" @down="downCallback"
|
||||
@up="upCallback" :down="downOption" :up="upOption">
|
||||
<!-- 未使用 -->
|
||||
<block v-if="status=='unuse'">
|
||||
<coupon-list :list="dataList" :checkout="false"></coupon-list>
|
||||
</block>
|
||||
<!-- 过期 已使用 -->
|
||||
<block v-else>
|
||||
<coupon-list disable :list="dataList" :checkout="false"></coupon-list>
|
||||
</block>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
export default {
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
status: 'unuse', //状态: unuse 未使用, used 已使用, expired 已过期
|
||||
downOption: {
|
||||
auto: false,
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20
|
||||
},
|
||||
noMoreSize: 1
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
height() {
|
||||
const {
|
||||
windowHeight,
|
||||
statusBarHeight
|
||||
} = this.$u.sys();
|
||||
return windowHeight - statusBarHeight - 44 + 'px';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
downCallback() {
|
||||
this.mescroll.resetUpScroll();
|
||||
this.dataList = []
|
||||
},
|
||||
async upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData({
|
||||
num,
|
||||
size
|
||||
}) {
|
||||
let obj = {
|
||||
status: this.status,
|
||||
page: num,
|
||||
per_page: size
|
||||
}
|
||||
this.$api.get(`/v1/coupons`, {
|
||||
params: obj
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.length)
|
||||
if (num == 1) this.dataList = [];
|
||||
this.dataList = this.dataList.concat(res.data);
|
||||
}).catch(err => {
|
||||
this.mescroll.endErr()
|
||||
})
|
||||
},
|
||||
handType(val) {
|
||||
this.status = val
|
||||
this.downCallback()
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
<template>
|
||||
<view class="text-md">
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<view :style="[{'opacity': nav0pacity}]" class="h-0 transition-opacity duration-20 z-900 fixed">
|
||||
<u-navbar title="粉丝列表"></u-navbar>
|
||||
</view>
|
||||
<view class="bg">
|
||||
<view class="status_bar" :style="{'height':statusBarHeight+'px'}"></view>
|
||||
<view class="p-base flex items-center">
|
||||
<view @tap="back">
|
||||
<image class="w-20rpx " src="/static/images/user/white_left_arrow.png" mode="widthFix"></image>
|
||||
</view>
|
||||
<view class="text-xl text-white flex-1 text-center">粉丝列表</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="w-full bg-white borderRarius ">
|
||||
<view class="px-base flex items-center">
|
||||
<view class=" relative -top-50rpx">
|
||||
<cu-avatar size="130" :src="user.avatar?user.avatar:''"></cu-avatar>
|
||||
</view>
|
||||
<view class="flex items-center -mt-60rpx ml-base">
|
||||
<view class=" text-txBase font-extrabold">用户名:</view>
|
||||
<view class=" text-txGray ml-15rpx">{{user.phone}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="-mt-30rpx flex items-center justify-between text-center text-txBase ">
|
||||
<view class="flex-1">粉丝人数</view>
|
||||
<view class="flex-1">成长值</view>
|
||||
</view>
|
||||
<view class="flex items-center justify-between text-center text-txBase font-extrabold">
|
||||
<view class="flex-1">{{fans_num}}</view>
|
||||
<view class="flex-1">{{growth_value}}</view>
|
||||
</view>
|
||||
<view class="bg-primary w-full h-10rpx mt-base"></view>
|
||||
</view>
|
||||
<mescroll-body :height="height" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
|
||||
:up="upOption">
|
||||
<view class="mt-base bg-white">
|
||||
<view v-if="dataList.length!=0" class=" flex items-center text-txBase py-base text-center">
|
||||
<view class="flex-1">用户名</view>
|
||||
<view class="flex-1">成长值</view>
|
||||
</view>
|
||||
<view class="pl-base">
|
||||
<view v-for="(item,index) in dataList" :key="index"
|
||||
class="flex border-t border-txBorder items-center text-center text-txGray py-base">
|
||||
<view class="flex-1 flex items-center ">
|
||||
<cu-avatar size="108" :src="item.avatar?item.avatar:''"></cu-avatar>
|
||||
<!-- <view class="w-108rpx h-108rpx rounded-full bg-txGray"></view> -->
|
||||
<view class="ml-base ">
|
||||
<view class="text-left">{{item.nickname?item.nickname:'匿名用户'}}</view>
|
||||
<view>{{item.phone|showPhone}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-1 flex items-center justify-center flex-col">
|
||||
<view>{{item.level_name}}</view>
|
||||
<view class="flex-1">{{item.value}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
export default {
|
||||
mixins: [MescrollMixin], // 使用mixin
|
||||
data() {
|
||||
return {
|
||||
isFirstLoading:true,
|
||||
scrollNumber: 0,
|
||||
fans_num:0,
|
||||
growth_value:"",
|
||||
downOption: {
|
||||
auto: false,
|
||||
use: false
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20
|
||||
},
|
||||
noMoreSize: 1
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
menuButtonInfo:0,//胶囊按钮信息
|
||||
statusBarHeight:0,//状态栏高度
|
||||
musicheadHeight:0
|
||||
};
|
||||
},
|
||||
onReady() {
|
||||
// #ifdef MP-WEIXIN
|
||||
//获取状态栏高度
|
||||
const { statusBarHeight,windowHeight,screenHeight }= uni.getSystemInfoSync()
|
||||
this.statusBarHeight=statusBarHeight
|
||||
// 获取胶囊按钮信息(width、height、top等)
|
||||
const {width,height,top}=uni.getMenuButtonBoundingClientRect()
|
||||
this.menuButtonInfo={width,height,top}
|
||||
// 胶囊按钮相对于离导航栏的上边距
|
||||
const topDistance=this.menuButtonInfo.top-this.statusBarHeight;
|
||||
// 计算导航栏高度
|
||||
this.musicheadHeight=this.menuButtonInfo.height+topDistance*2;
|
||||
// #endif
|
||||
},
|
||||
onPageScroll(e) {
|
||||
this.scrollNumber = e.scrollTop
|
||||
},
|
||||
computed: {
|
||||
nav0pacity() {
|
||||
const option = (this.scrollNumber - 10) / 100
|
||||
return option >= 1 ? 1 : option
|
||||
},
|
||||
user() {
|
||||
return this.$store.getters.user ?? {}
|
||||
},
|
||||
height() {
|
||||
const {
|
||||
windowHeight,
|
||||
statusBarHeight
|
||||
} = this.$u.sys();
|
||||
return windowHeight - statusBarHeight - 270 + 'px';
|
||||
},
|
||||
},
|
||||
onLoad() {
|
||||
this.getFans()
|
||||
},
|
||||
methods: {
|
||||
async getFans() {
|
||||
try{
|
||||
let resDate = await this.$api.get('/v1/fans/statistics', {})
|
||||
this.fans_num = resDate.fans_num
|
||||
this.growth_value = resDate.growth_value
|
||||
}catch(err){}finally{
|
||||
this.isFirstLoading=false
|
||||
}
|
||||
},
|
||||
downCallback() {
|
||||
this.mescroll.resetUpScroll();
|
||||
this.dataList = []
|
||||
},
|
||||
async upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData(page) {
|
||||
this.$api.get(`/v1/fans`, {
|
||||
params: {
|
||||
page: page.num,
|
||||
per_page: page.size
|
||||
}
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.length)
|
||||
if (page.num == 1) this.dataList = [];
|
||||
this.dataList = this.dataList.concat(res.data);
|
||||
}).catch(err => {
|
||||
this.mescroll.endErr()
|
||||
})
|
||||
},
|
||||
back() {
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.status_bar {
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bg {
|
||||
width: 100%;
|
||||
height: 407rpx;
|
||||
background-image: url(../../static/images/user/fan_title_bg.png);
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.borderRarius {
|
||||
border-top-left-radius: 30rpx;
|
||||
border-top-right-radius: 30rpx;
|
||||
margin-top: -150rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<template>
|
||||
<view>
|
||||
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
|
||||
:up="upOption">
|
||||
<block v-for="(ite,ind) in dataList" :key="ind">
|
||||
<view class="p-base text-hex-A6A6A6 text-md">{{ite.view_date}}</view>
|
||||
<view class="px-10rpx">
|
||||
<u-waterfall v-model="ite.list" ref="uWaterfall">
|
||||
<template v-slot:left="{ leftList }">
|
||||
<view v-for="(item, index) in leftList" :key="index" class="px-rowSm mb-base">
|
||||
<goods-item :goods="item.sku"></goods-item>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:right="{ rightList }">
|
||||
<view v-for="(item, index) in rightList" :key="index" class="px-rowSm mb-base">
|
||||
<goods-item :goods="item.sku"></goods-item>
|
||||
</view>
|
||||
</template>
|
||||
</u-waterfall>
|
||||
</view>
|
||||
</block>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
export default {
|
||||
mixins: [MescrollMixin], // 使用mixin
|
||||
data() {
|
||||
return {
|
||||
downOption: {
|
||||
auto: false,
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20
|
||||
},
|
||||
noMoreSize: 1
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
downCallback() {
|
||||
this.mescroll.resetUpScroll();
|
||||
this.dataList = []
|
||||
this.$refs.uWaterfall[0].clear();
|
||||
},
|
||||
async upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData(page) {
|
||||
this.$api.get(`/v1/product/view-logs`, {
|
||||
params: {
|
||||
page: page.num,
|
||||
per_page: page.size
|
||||
}
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.length)
|
||||
if (page.num == 1) this.dataList = [];
|
||||
this.gethandle(res.data)
|
||||
// this.dataList = this.dataList.concat(res.data);
|
||||
}).catch(err => {
|
||||
this.mescroll.endErr()
|
||||
})
|
||||
},
|
||||
//处理数据
|
||||
gethandle(arr) {
|
||||
let tempArr = [],
|
||||
newArr = []
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (tempArr.indexOf(arr[i].view_date) === -1) {
|
||||
newArr.push({
|
||||
view_date: arr[i].view_date,
|
||||
list: [arr[i]]
|
||||
})
|
||||
tempArr.push(arr[i].view_date);
|
||||
} else {
|
||||
for (let j = 0; j < newArr.length; j++) {
|
||||
if (newArr[j].view_date == arr[i].view_date) {
|
||||
newArr[j].list.push(arr[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.dataList = this.dataList.concat(newArr);
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,376 @@
|
|||
<template>
|
||||
<view class="py-base">
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<view class="w-710rpx mx-auto flex flex-col">
|
||||
<swiper @change="swiperChange" class="h-1160rpx" :indicator-dots="false" :autoplay="false" :interval="interval"
|
||||
:duration="duration">
|
||||
<swiper-item class="h-full" v-for="(item,index) in bgImage" :key="index">
|
||||
<view class="h-full invite-bg relative">
|
||||
<image :src="item.image" class="w-full h-full"></image>
|
||||
<view
|
||||
class="flex-1 flex items-center justify-between px-30rpx absolute bottom-0rpx bg-white w-full rounded-t-20rpx py-base">
|
||||
<view class="flex items-start flex-col">
|
||||
<view class="-mt-60rpx">
|
||||
<cu-avatar size="100" :src="user.avatar?user.avatar:''"></cu-avatar>
|
||||
</view>
|
||||
<view class="mt-10rpx text-txGray">{{showPhone}}</view>
|
||||
<view class="font-extrabold text-xl">子春生</view>
|
||||
<view>{{item.remark?item.remark:''}}</view>
|
||||
</view>
|
||||
<view>
|
||||
<image class="w-168rpx h-168rpx" :src="code" mode="scaleToFill" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
<!-- 指示点 -->
|
||||
<view v-if="bgImage.length>1" class="flex items-center justify-center mt-30rpx">
|
||||
<view :class="dotIndex==index?'dotBg':''" v-for="(item,index) in bgImage.length" :key="index"
|
||||
class="rounded-full mx-10rpx w-20rpx h-20rpx bg-hex-a9a9a9"></view>
|
||||
</view>
|
||||
<view class="mt-43rpx px-80rpx">
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<!-- <view class="login-btn" @tap="saveImgToLocal">保存到相册</view> -->
|
||||
<view class=" grid grid-cols-3 text-txGray text-26rpx">
|
||||
<view @tap="sharingFriend(0)" class="flex items-center justify-center flex-col">
|
||||
<view class="w-110rpx h-110rpx flex items-center justify-center flex-col rounded-15rpx bg-hex-00dd00 ">
|
||||
<image class="w-66rpx h-66rpx" src="/static/images/share/wechat-friends.png" mode=""></image>
|
||||
</view>
|
||||
<view class="mt-10rpx">微信好友</view>
|
||||
</view>
|
||||
<view @tap="sharingFriend(1)" class="flex items-center justify-center flex-col">
|
||||
<view class="w-110rpx h-110rpx flex items-center justify-center flex-col rounded-15rpx bg-hex-ffffff">
|
||||
<image class="w-66rpx h-66rpx" src="/static/images/share/wechat-moments.png" mode=""></image>
|
||||
</view>
|
||||
<view class="mt-10rpx">朋友圈</view>
|
||||
</view>
|
||||
<view @tap="saveImgToLocal" class="flex items-center justify-center flex-col">
|
||||
<view class="w-110rpx h-110rpx flex items-center justify-center flex-col rounded-15rpx bg-hex-fcbd71">
|
||||
<image class="w-66rpx h-66rpx" src="/static/images/share/album.png" mode=""></image>
|
||||
</view>
|
||||
|
||||
<view class="mt-10rpx">相册</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view class="login-btn" @tap="saveImgToLocal">点击下载长按保存到相册</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- <view class="login-btn" @tap="saveImgToLocal">保存图片</view> -->
|
||||
<view class=" grid grid-cols-2 text-txGray text-26rpx">
|
||||
<button hover-class="none" open-type="share" class="share-btn cu-btn flex items-center justify-center flex-col">
|
||||
<view class="w-110rpx h-110rpx flex items-center justify-center flex-col rounded-15rpx bg-hex-00dd00 ">
|
||||
<image class="w-66rpx h-66rpx" src="/static/images/share/wechat-friends.png" mode=""></image>
|
||||
</view>
|
||||
<view class="mt-10rpx">微信好友</view>
|
||||
</button>
|
||||
<view @tap="saveImgToLocal" class="flex items-center justify-center flex-col">
|
||||
<view class="w-110rpx h-110rpx flex items-center justify-center flex-col rounded-15rpx bg-hex-fcbd71">
|
||||
<image class="w-66rpx h-66rpx" src="/static/images/share/album.png" mode=""></image>
|
||||
</view>
|
||||
<view class="mt-10rpx">保存到相册</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<canvas class="hideCanvas" canvas-id="default_PosterCanvasId"
|
||||
:style="{width: (poster.width||0) + 'px', height: (poster.height||0) + 'px'}"></canvas>
|
||||
<!-- 提示框 -->
|
||||
<cu-modal title="提示" v-model="showModel" content="没有授权保存图片到相册,点击确定授权" show-cancel-button async-close
|
||||
@confirm="onConfirm"></cu-modal>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
getSharePoster
|
||||
} from '@/components/QS-SharePoster/QS-SharePoster.js';
|
||||
import permision from "@/utils/permission.js"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
showModel: false,
|
||||
isFirstLoading: true,
|
||||
bgImage: [],
|
||||
qr_code: '',
|
||||
interval: 2000,
|
||||
duration: 500,
|
||||
dotIndex: 0,
|
||||
poster: {},
|
||||
url: '', //合成的海报
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.getters.user ?? {}
|
||||
},
|
||||
showPhone() {
|
||||
return this.user.phone?.replace(/^(\d{3})\d+(\d{4})$/, '$1****$2')
|
||||
},
|
||||
posterbg() {
|
||||
return this.bgImage[this.dotIndex]?.image ?? ''
|
||||
},
|
||||
code() {
|
||||
return 'data:image/png;base64,' + this.qr_code
|
||||
},
|
||||
remark() {
|
||||
return this.bgImage[this.dotIndex]?.remark ?? ''
|
||||
},
|
||||
},
|
||||
onLoad() {
|
||||
this.getImage()
|
||||
this.getCode()
|
||||
},
|
||||
//微信分享
|
||||
onShareAppMessage(){
|
||||
let code = this.user.code ? this.user.code : '';
|
||||
let shareObj={
|
||||
title:'我正在使用子春生小程序,赶紧跟我一起来体验吧!',
|
||||
path:`/pages/index/index?invite_code=${code}`,
|
||||
imageUrl:''
|
||||
}
|
||||
return shareObj
|
||||
},
|
||||
methods: {
|
||||
///分享到微信
|
||||
sharingFriend(val) {
|
||||
let scene = val == 0 ? 'WXSceneSession' : 'WXSceneTimeline'
|
||||
uni.share({
|
||||
provider: "weixin",
|
||||
scene: scene,
|
||||
type: 0,
|
||||
href: `https://wap.zichunsheng.cn/register?code=${this.user.code}`,
|
||||
title: '我正在使用子春生APP,赶紧跟我一起来体验吧!',
|
||||
// summary: "我正在使用子春生APP,赶紧跟我一起来体验吧!",
|
||||
imageUrl: '/static/images/share/logs.png',
|
||||
success: res => {}
|
||||
})
|
||||
},
|
||||
//获取背景图
|
||||
async getImage() {
|
||||
try {
|
||||
let resDate = await this.$api.get('/v1/share-bgs', {})
|
||||
this.bgImage = resDate
|
||||
} catch (err) {} finally {
|
||||
this.isFirstLoading = false
|
||||
}
|
||||
},
|
||||
async getCode() {
|
||||
let {
|
||||
qr_code
|
||||
} = await this.$api.post('/v1/user-qr-code', {})
|
||||
this.qr_code = qr_code
|
||||
},
|
||||
swiperChange(e) {
|
||||
this.dotIndex = e.detail.current
|
||||
},
|
||||
async shareFc() {
|
||||
uni.showLoading({
|
||||
title: '图片合成中...'
|
||||
});
|
||||
let t = this;
|
||||
try {
|
||||
const d = await getSharePoster({
|
||||
_this: this, //若在组件中使用 必传
|
||||
type: 'testShareType',
|
||||
background: {
|
||||
height: 670, //画布高度
|
||||
width: 385, //画布宽度
|
||||
backgroundColor: '#ffffff', //画布背景颜色
|
||||
},
|
||||
setCanvasWH({
|
||||
bgObj
|
||||
}) { //一般必传, 动态设置canvas宽高
|
||||
_this.poster = bgObj
|
||||
},
|
||||
posterCanvasId: 'default_PosterCanvasId', //canvasId
|
||||
delayTimeScale: 2, //延时系数
|
||||
drawArray: ({
|
||||
setBgObj,
|
||||
getBgObj,
|
||||
type,
|
||||
bgScale
|
||||
}) => {
|
||||
return new Promise((rs, rj) => {
|
||||
rs([{
|
||||
type: 'image',
|
||||
url: t.posterbg,
|
||||
infoCallBack(imageInfo) {
|
||||
return {
|
||||
dx: 0,
|
||||
dy: 0,
|
||||
dWidth: 385,
|
||||
dHeight: 550,
|
||||
}
|
||||
}
|
||||
}, //顶部图片
|
||||
{
|
||||
type: 'image',
|
||||
url: t.user.avatar ? t.user.avatar : '/static/images/user/avatar.png',
|
||||
roundRectSet: {
|
||||
r: 54
|
||||
},
|
||||
infoCallBack(imageInfo) {
|
||||
return {
|
||||
dx: 15,
|
||||
dy: 530,
|
||||
dWidth: 54,
|
||||
dHeight: 54,
|
||||
}
|
||||
}
|
||||
}, //头像
|
||||
{
|
||||
type: 'text',
|
||||
text: t.showPhone,
|
||||
size: '14',
|
||||
color: '#808080',
|
||||
infoCallBack(imageInfo) {
|
||||
return {
|
||||
dx: 15,
|
||||
dy: 600,
|
||||
}
|
||||
}
|
||||
}, //电话
|
||||
{
|
||||
type: 'text',
|
||||
text: '子春生',
|
||||
size: '16',
|
||||
color: '#383838',
|
||||
fontWeight: 'bold',
|
||||
infoCallBack(imageInfo) {
|
||||
return {
|
||||
dx: 15,
|
||||
dy: 620,
|
||||
}
|
||||
}
|
||||
}, //公司名称
|
||||
{
|
||||
type: 'text',
|
||||
text: t.remark,
|
||||
size: '14',
|
||||
color: '#383838',
|
||||
infoCallBack(imageInfo) {
|
||||
return {
|
||||
dx: 15,
|
||||
dy: 645,
|
||||
}
|
||||
}
|
||||
}, //简介
|
||||
{
|
||||
type: 'image',
|
||||
// url: t.code,
|
||||
url: t.code,
|
||||
infoCallBack(imageInfo) {
|
||||
return {
|
||||
dx: 275,
|
||||
dy: 570,
|
||||
dWidth: 84,
|
||||
dHeight: 84,
|
||||
}
|
||||
}
|
||||
}, //二维码
|
||||
]);
|
||||
})
|
||||
},
|
||||
setCanvasWH: ({
|
||||
bgObj,
|
||||
type,
|
||||
bgScale
|
||||
}) => { // 为动态设置画布宽高的方法,
|
||||
t.poster = bgObj;
|
||||
}
|
||||
});
|
||||
t.url = d.poster.tempFilePath;
|
||||
uni.hideLoading()
|
||||
} catch (e) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
},
|
||||
//保存图片(小程序,app)
|
||||
saveImage() {
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: this.url,
|
||||
success: res => {
|
||||
this.$u.toast("保存图片成功")
|
||||
},
|
||||
})
|
||||
},
|
||||
//h5--保存
|
||||
pressSave() {
|
||||
let arr = []
|
||||
arr.push(this.url)
|
||||
uni.previewImage({
|
||||
urls: arr
|
||||
})
|
||||
},
|
||||
async saveImgToLocal() {
|
||||
this.$u.throttle(async () => {
|
||||
// #ifdef MP-WEIXIN
|
||||
await this.getWinxinAlbum()
|
||||
// #endif
|
||||
//#ifdef APP-PLUS
|
||||
await this.shareFc()
|
||||
await this.saveImage()
|
||||
//#endif
|
||||
//#ifdef H5
|
||||
await this.shareFc()
|
||||
await this.pressSave()
|
||||
//#endif
|
||||
}, 1000)
|
||||
},
|
||||
//判断小程序相册功能是否打开
|
||||
getWinxinAlbum() {
|
||||
uni.authorize({
|
||||
scope: 'scope.writePhotosAlbum',
|
||||
success: async res => {
|
||||
await this.shareFc()
|
||||
await this.saveImage()
|
||||
},
|
||||
fail: (comp) => {
|
||||
uni.getSetting({
|
||||
success: Setting => {
|
||||
if (!Setting.authSetting['scope.writePhotosAlbum']) {
|
||||
this.showModel = true
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
onConfirm() {
|
||||
this.showModel = false
|
||||
uni.openSetting({
|
||||
success: res => {
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.share-btn::after{ border: none; }
|
||||
.dotBg {
|
||||
background: #000000 !important;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.39);
|
||||
}
|
||||
|
||||
.invite-bg {
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.hideCanvas {
|
||||
position: fixed;
|
||||
top: -9999999px;
|
||||
left: -9999999px;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
<template>
|
||||
<view class="pb-base">
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<view class="tops">
|
||||
<image class="w-full h-390rpx " src="/static/images/member/member_top.png" mode="widthFix"></image>
|
||||
</view>
|
||||
<view @tap="jumpByOption(cpaceImage)" class="w-full" v-if="cpaceImage.image">
|
||||
<image class="w-full" :src="cpaceImage.image" mode="widthFix"></image>
|
||||
</view>
|
||||
<!-- -->
|
||||
<view class="bgs w-full mt-10rpx p-base" v-if="vip_coupon_bottom_banner.length!=0">
|
||||
<view class="text-lg text-hex-FFCFAB">会员直通车购买 立减40元</view>
|
||||
<scroll-view scroll-x="true" class="scroll mt-base">
|
||||
<view @tap="jumpByOption(item)" class="scroll-item fristChild p-25rpx bg-white rounded-15rpx overflow-hidden" v-for="(item,index) in vip_coupon_bottom_banner" :key="index">
|
||||
<image class="w-240rpx "
|
||||
:src="item.image" mode="widthFix"></image>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- -->
|
||||
<view v-if="swiperList.length!=0" class="bgs w-full mt-base p-base ">
|
||||
<view class="text-lg text-hex-FFCFAB">会员补贴次次省 一年累计可省8000元左右</view>
|
||||
<swiper circular class="mt-30rpx h-380rpx" :autoplay="true" :interval="3000" :duration="1000">
|
||||
<swiper-item v-for="(item,index) in swiperList" :key="index" class="w-710rpx bg-white rounded-15rpx p-base">
|
||||
<view class="w-full h-full" @tap="jumpByOption(item)">
|
||||
<image class="w-full h-full" :src="item.image" mode="aspectFill"></image>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
<!-- -->
|
||||
<block v-if="interestItem.image">
|
||||
<view class="bgs w-full mt-base p-base">
|
||||
<view class="text-lg text-hex-FFCFAB">会员积分权益</view>
|
||||
<view @tap="jumpByOption(interestItem)">
|
||||
<image class="w-full mt-base" :src="interestItem.image" mode="widthFix"></image>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<!-- -->
|
||||
<block v-if="rewardList.length!=0 || scrollList.length!=0">
|
||||
<view class="bgs w-full mt-base p-base">
|
||||
<view class="text-lg text-hex-FFCFAB">分享商品得奖励 一年累计可赚20000元左右</view>
|
||||
<block v-if="rewardList.length!=0">
|
||||
<scroll-view scroll-x="true" class="scroll mt-base">
|
||||
<view @tap="jumpByOption(item)" class="scroll-item fristChild" v-for="(item,index) in rewardList" :key="index">
|
||||
<image class=" w-226rpx h-289rpx"
|
||||
:src="item.image" mode=""></image>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</block>
|
||||
<view class="mt-base" v-if="scrollList.length!=0">
|
||||
<scroll-view scroll-x="true" class="scroll">
|
||||
<view @tap="jumpByOption(item)" class="scroll-item fristChild" v-for="(item,index) in scrollList" :key="index">
|
||||
<image class="w-470rpx h-200rpx rounded-15rpx"
|
||||
:src="item.image" mode=""></image>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<!-- -->
|
||||
<view v-if="text" class="bgs w-full mt-base p-base pb-50rpx">
|
||||
<view class="text-center text-lg text-hex-FFCFAB">推广细则</view>
|
||||
<view class="line"></view>
|
||||
<view class=" bg-hex-fce3cf rounded-15rpx -mt-5rpx p-40rpx text-md text-txBase">
|
||||
<text>{{text}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view @tap="$u.route('pageA/special_area/index')" class="fixed z-50 top-700rpx right-0">
|
||||
<image class="w-184rpx h-196rpx" src="/static/images/member/suspension.png" mode=""></image>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isFirstLoading:true,
|
||||
vip_coupon_bottom_banner:[],
|
||||
interests:[],
|
||||
swiperList: [],
|
||||
list: [],
|
||||
text: '',
|
||||
scrollList: [],
|
||||
rewardList: []
|
||||
};
|
||||
},
|
||||
computed:{
|
||||
interestItem(){
|
||||
return this.interests.length>0?this.interests[0]:{}
|
||||
},
|
||||
cpaceImage(){
|
||||
return this.list.length>0?this.list[0]:{}
|
||||
},
|
||||
},
|
||||
onLoad() {
|
||||
this.getvipCouponBanner()
|
||||
this.getCpace()
|
||||
this.advertisement()
|
||||
this.getreward()
|
||||
this.province()
|
||||
this.promotionAward()
|
||||
this.getCoupon()
|
||||
},
|
||||
methods: {
|
||||
async getvipCouponBanner(){
|
||||
let resDate = await this.$api.get('/v1/ads', {
|
||||
params: {
|
||||
keys: 'wechat_mini_vip_coupon_bottom_banner'
|
||||
}
|
||||
})
|
||||
this.vip_coupon_bottom_banner=resDate.wechat_mini_vip_coupon_bottom_banner
|
||||
},
|
||||
//顶部广告位
|
||||
async getCpace(){
|
||||
let resDate = await this.$api.get('/v1/ads', {
|
||||
params: {
|
||||
keys: 'wechat_mini_vip_coupon_banner'
|
||||
}
|
||||
})
|
||||
this.list=resDate.wechat_mini_vip_coupon_banner
|
||||
},
|
||||
//会员就是省
|
||||
async province(){
|
||||
let resDate = await this.$api.get('/v1/ads', {
|
||||
params: {
|
||||
keys: 'wechat_mini_vip_will_cheap_banner'
|
||||
}
|
||||
})
|
||||
this.swiperList=resDate.wechat_mini_vip_will_cheap_banner
|
||||
},
|
||||
//会员推广奖励
|
||||
async promotionAward(){
|
||||
try{
|
||||
let resDate = await this.$api.get('/v1/ads', {
|
||||
params: {
|
||||
keys: 'wechat_mini_vip_award_bottom_banner'
|
||||
}
|
||||
})
|
||||
this.scrollList=resDate.wechat_mini_vip_award_bottom_banner
|
||||
}catch(err){}finally{
|
||||
// this.isFirstLoading=false
|
||||
}
|
||||
|
||||
},
|
||||
//获取优惠卷
|
||||
async getCoupon() {
|
||||
try{
|
||||
let resDate = await this.$api.get('/v1/product/part-coupons', {
|
||||
params: {
|
||||
part: 'vip'
|
||||
}
|
||||
})
|
||||
// this.list = resDate.coupons
|
||||
this.text = resDate.description
|
||||
}catch(err){}finally{
|
||||
this.isFirstLoading=false
|
||||
}
|
||||
},
|
||||
//
|
||||
async advertisement() {
|
||||
let resDate = await this.$api.get('/v1/ads', {
|
||||
params: {
|
||||
keys: 'wechat_mini_vip_banner'
|
||||
}
|
||||
})
|
||||
this.interests = resDate.wechat_mini_vip_banner
|
||||
},
|
||||
async getreward() {
|
||||
let resDate = await this.$api.get('/v1/ads', {
|
||||
params: {
|
||||
keys: 'wechat_mini_vip_award_banner '
|
||||
}
|
||||
})
|
||||
this.rewardList = resDate.wechat_mini_vip_award_banner
|
||||
},
|
||||
jumpByOption(e) {
|
||||
if (!!e.jump_link) {
|
||||
if (e.jump_type == 1) {
|
||||
this.$u.route(e.jump_link);
|
||||
} else if (e.jump_type == 2) {
|
||||
this.$u.route(`/pages/web_view/index?url=${e.jump_link}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tops {
|
||||
height: 350rpx;
|
||||
background: linear-gradient(180deg, rgba(50, 49, 55, 1) 0%, rgba(245, 245, 245, 1) 100%);
|
||||
}
|
||||
|
||||
.mem_bg_one {
|
||||
width: 100%;
|
||||
height: 524rpx;
|
||||
background-image: url('../../static/images/member/member_1.png');
|
||||
background-size: auto 100%;
|
||||
}
|
||||
|
||||
.men_bg_two {
|
||||
width: 210rpx;
|
||||
height: 210rpx;
|
||||
background-image: url('../../static/images/member/red.png');
|
||||
background-size: auto 100%;
|
||||
}
|
||||
|
||||
.after {
|
||||
&::before {
|
||||
content: '¥';
|
||||
font-size: 60%;
|
||||
}
|
||||
}
|
||||
.afters {
|
||||
&::after {
|
||||
content: '折';
|
||||
font-size: 60%;
|
||||
}
|
||||
}
|
||||
.bgs {
|
||||
background: linear-gradient(90deg, rgba(70, 68, 76, 1) 0%, rgba(47, 46, 51, 1) 100%);
|
||||
// @apply rounded-xs
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
|
||||
.fristChild:not(:first-child) {
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.line {
|
||||
width: 195rpx;
|
||||
height: 15rpx;
|
||||
margin: 0 auto;
|
||||
background-color: rgba(255, 207, 171, 1);
|
||||
border-radius: 90rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
<template>
|
||||
<view>
|
||||
<!-- 导航栏 -->
|
||||
<u-navbar back-icon-color="#000000" title-color="#000000" :border-bottom="false">
|
||||
<view class="flex-1 text-center text-32rpx">我的账户</view>
|
||||
<view slot="right" class="pr-base" @tap="show=!show,tipsShow=false">
|
||||
<u-icon name="list-dot" size="48"></u-icon>
|
||||
</view>
|
||||
</u-navbar>
|
||||
<!-- -->
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<view class="flex items-center justify-between px-base pt-base">
|
||||
<view class="w-345rpx flex items-center justify-center flex-col h-191rpx bg-hex-E4F1F2 box-shadow ">
|
||||
<view class="flex items-center justify-center">
|
||||
<image class="w-89rpx h-89rpx" src="/static/images/user/pre_income.png" mode=""></image>
|
||||
<view class="ml-base text-xl text-hex-00A2B0 flex items-center justify-center flex-col">
|
||||
<view>{{account.distribution_pre|numFormat}}</view>
|
||||
<view>预收益</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="text-20rpx mt-10rpx text-hex-00A2B0">{{account.distribution_pre}}</view> -->
|
||||
</view>
|
||||
<view class="w-345rpx h-191rpx bg-hex-E9ECFF box-shadow flex items-center flex-col justify-center">
|
||||
<view class="flex items-center justify-center">
|
||||
<image class="w-89rpx h-89rpx" src="/static/images/user/can_mention.png" mode=""></image>
|
||||
<view class="ml-base text-xl text-hex-635DF7 flex items-center justify-center flex-col">
|
||||
<view>{{account.wallet_balance|numFormat}}</view>
|
||||
<view>可提</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="text-20rpx mt-10rpx text-hex-635DF7">{{account.wallet_balance}}</view> -->
|
||||
</view>
|
||||
</view>
|
||||
<!-- -->
|
||||
<view class="mt-30rpx flex items-center bg-hex-F7F7F7 sticky suspension zIndex">
|
||||
<view @tap="handType(item.type)" class="flex-1 py-base text-center text-lg text-txGray"
|
||||
:class="type==item.type?'active' :''" v-for="(item,index) in list" :key="index"> {{item.name}}</view>
|
||||
</view>
|
||||
<mescroll-body :height="height" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
|
||||
:up="upOption">
|
||||
<view class=" bg-white">
|
||||
<view class="py-base flex text-lg text-txGray items-center">
|
||||
<view class="flex-1 text-center">信息</view>
|
||||
<view v-if="type!=1" class="flex-1 text-center">时间</view>
|
||||
<view v-if="type==1" class="flex-1 text-center">状态</view>
|
||||
<view class="flex-1 text-center">金额</view>
|
||||
</view>
|
||||
<view @tap="tipsPopup(item)" class="flex items-center text-txGray text-lg py-base" v-for="(item,index) in dataList"
|
||||
:key="index">
|
||||
<view class="flex-1 text-center flex justify-center ">
|
||||
<view class="one-ellipsis w-180rpx">
|
||||
{{item.remarks}}
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="type!=1" class="flex-1 text-center">{{item.created_at}}</view>
|
||||
<view v-if="type==1" class="flex-1 text-center">{{item.status}}</view>
|
||||
<view v-if="type==1" class="flex-1 text-center">{{item.total_revenue}}</view>
|
||||
<view v-else class="flex-1 text-center">{{item.change_balance}}</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
<!-- 主菜单弹窗 -->
|
||||
<view @touchmove.stop.prevent="" :style="{top:top+'px'} " @tap="show=!show" v-if="show"
|
||||
class="fixed left-0 right-0 h-full z-999 bg-opacity-60 bg-hex-000000 ">
|
||||
<view class="px-base flex justify-end mt-10rpx items-end flex-col">
|
||||
<view class="triangle-up mr-base"></view>
|
||||
<view @tap.stop="" class="px-43rpx pt-10rpx bg-white rounded-15rpx text-lg text-txGray pb-30rpx flex-col">
|
||||
<view @tap.stop="jump('pageA/balance_transfer/index')" hover-class="text-primary" class=" pt-base ">余额转账
|
||||
</view>
|
||||
<view @tap.stop="jump('pageA/withdrawal_balance/index')" hover-class="text-primary" class="pt-base ">
|
||||
可提转余额</view>
|
||||
<view @tap.stop="jump('pageA/bank_card/index')" hover-class="text-primary" class="pt-base ">提现到银行卡
|
||||
</view>
|
||||
<view @tap.stop="jump('pageA/my_bank/index')" hover-class="text-primary" class="pt-base ">我的银行卡</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 提示框 -->
|
||||
<view @touchmove.stop.prevent="" v-if="tipsShow" :style="{top:top+'px'} "
|
||||
class="fixed left-0 right-0 h-full z-999 bg-opacity-60 bg-hex-000000 ">
|
||||
<view class="w-710rpx max-710rpx bg-white m-auto mt-300rpx rounded-xs overflow">
|
||||
<view @tap="tipsShow=false" class="h-70rpx bg-hex-F7F7F7 flex items-center justify-end px-25rpx">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/user/account_del.png" mode="aspectFill"></image>
|
||||
</view>
|
||||
<!-- -->
|
||||
<scroll-view scroll-y="true" class="max-h-640rpx">
|
||||
<view class="px-base py-40rpx" >
|
||||
<block v-if="type!=1">
|
||||
<u-time-line>
|
||||
<u-time-line-item >
|
||||
<template v-slot:node >
|
||||
<view></view>
|
||||
</template>
|
||||
<template v-slot:content>
|
||||
<view class="font-extrabold">
|
||||
<view class="u-order-title ">{{current.remarks}}</view>
|
||||
<view class="u-order-desc ">时间:{{current.created_at}}</view>
|
||||
<view class="u-order-time ">金额:{{current.change_balance}}</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-time-line-item>
|
||||
</u-time-line>
|
||||
</block>
|
||||
<block v-if="type==1">
|
||||
<u-time-line>
|
||||
<u-time-line-item v-for="(item,index) in current.logs" :key="index">
|
||||
<template v-slot:node >
|
||||
<view v-if="index==0"></view>
|
||||
</template>
|
||||
<template v-slot:content>
|
||||
<view :class="index==0?'font-extrabold':''">
|
||||
<view class="u-order-title ">{{item.remarks}}</view>
|
||||
<view class="u-order-desc ">金额:{{item.change_amount}}</view>
|
||||
<view class="u-order-time ">收益:{{item.change_revenue}}</view>
|
||||
<view class="u-order-time ">时间:{{item.created_at}}</view>
|
||||
<view class="pt-10rpx">
|
||||
<u-line v-if="index != current.logs.length - 1" :hair-line="false" color="#808080" border-style="dashed"></u-line>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-time-line-item>
|
||||
</u-time-line>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
export default {
|
||||
mixins: [MescrollMixin], // 使用mixin
|
||||
data() {
|
||||
return {
|
||||
isFirstLoading:true,
|
||||
type: 1,
|
||||
show: false,
|
||||
tipsShow: false,
|
||||
list: [{
|
||||
name: '预收益',
|
||||
type: 1
|
||||
},
|
||||
{
|
||||
name: '可提',
|
||||
type: 2
|
||||
},
|
||||
{
|
||||
name: '余额',
|
||||
type: 3
|
||||
}
|
||||
],
|
||||
account: {},
|
||||
downOption: {
|
||||
auto: false,
|
||||
use: false
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20
|
||||
},
|
||||
noMoreSize: 1
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
logs:[],//查看明细
|
||||
current:{}
|
||||
};
|
||||
},
|
||||
onShow() {
|
||||
this.getMoney()
|
||||
},
|
||||
computed: {
|
||||
top() {
|
||||
const {
|
||||
statusBarHeight
|
||||
} = this.$u.sys();
|
||||
return statusBarHeight + 44;
|
||||
},
|
||||
height() {
|
||||
const {
|
||||
windowHeight,
|
||||
statusBarHeight
|
||||
} = this.$u.sys();
|
||||
return windowHeight - statusBarHeight - 250 + 'px';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
//获取账户余额
|
||||
async getMoney() {
|
||||
try{
|
||||
let resDate = await this.$api.get('/v1/wallet', {})
|
||||
this.account = resDate
|
||||
}catch(err){}finally{
|
||||
this.isFirstLoading=false
|
||||
}
|
||||
},
|
||||
handType(val) {
|
||||
this.type = val
|
||||
this.dataList = []
|
||||
this.mescroll.scrollTo(0, 0);
|
||||
this.mescroll.resetUpScroll()
|
||||
},
|
||||
async upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData(page) {
|
||||
let url
|
||||
switch (this.type) {
|
||||
case 1:
|
||||
url = '/v1/wallet/distribution-logs'
|
||||
break;
|
||||
case 2:
|
||||
url = '/v1/wallet/wallet-logs'
|
||||
break;
|
||||
case 3:
|
||||
url = '/v1/wallet/balance-logs'
|
||||
break;
|
||||
}
|
||||
this.$api.get(url, {
|
||||
params: {
|
||||
page: page.num,
|
||||
per_page: page.size
|
||||
}
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.length)
|
||||
if (page.num == 1) this.dataList = [];
|
||||
this.dataList = this.dataList.concat(res.data);
|
||||
}).catch(err => {
|
||||
this.mescroll.endErr()
|
||||
})
|
||||
},
|
||||
tipsPopup(e) {
|
||||
this.current = e
|
||||
// this.logs = e?.logs ?? []
|
||||
this.tipsShow=true
|
||||
},
|
||||
jump(url) {
|
||||
this.$u.routeAuth(url)
|
||||
this.show = false
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.zIndex{
|
||||
z-index: 999 !important;
|
||||
}
|
||||
.box-shadow {
|
||||
box-shadow: 0px 2rpx 10rpx rgba(0, 0, 0, 0.15);
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
|
||||
.active {
|
||||
@apply text-primary bg-white relative;
|
||||
&:after{
|
||||
content: '';
|
||||
@apply absolute bottom-0 h-4rpx w-full bg-primary left-0
|
||||
}
|
||||
}
|
||||
|
||||
.triangle-up {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 15rpx solid transparent;
|
||||
border-right: 15rpx solid transparent;
|
||||
border-bottom: 30rpx solid #FFFFFF;
|
||||
}
|
||||
|
||||
.u-time-axis-item {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.suspension {
|
||||
top: 88px;
|
||||
// #ifdef H5
|
||||
top: 44px // #endif
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
<template>
|
||||
<view>
|
||||
<nav-shadow></nav-shadow>
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<view class="text-center text-txBase text-xl pt-80rpx font-extrabold">请填写银行卡信息</view>
|
||||
<view class="px-60rpx ">
|
||||
<view class="flex items-center text-txBase text-lg mt-60rpx">
|
||||
<view class="w-120rpx">持卡人</view>
|
||||
<input :disabled="is_edited" v-model="real_name" placeholder-class="text-txGray" class="inputHeight rounded-lg" type="text" placeholder="持卡人">
|
||||
</view>
|
||||
<view v-if="!is_edited" class="text-right text-sm text-hex-D43030">*持卡人名称仅修改一次</view>
|
||||
<view class="flex items-center text-txBase text-lg mt-60rpx">
|
||||
<view class="w-120rpx">银行卡</view>
|
||||
<input v-model="bank_number" placeholder-class="text-txGray" class="inputHeight rounded-lg" maxlength="25" type="number" placeholder="请填写银行卡号">
|
||||
</view>
|
||||
<view @tap="bankShow=true" class="flex items-center text-lg mt-60rpx">
|
||||
<view class="w-120rpx">银行</view>
|
||||
<view v-if="bank_name" class="inputHeight rounded-lg text-txBase leading-80rpx ">{{bank_name}}</view>
|
||||
<view v-else class="inputHeight rounded-lg leading-80rpx text-txGray">请选择所属银行</view>
|
||||
</view>
|
||||
<view class="flex items-center text-txBase text-lg mt-60rpx">
|
||||
<view class="w-120rpx">开户行</view>
|
||||
<input v-model="bank_description" placeholder-class="text-txGray" class="inputHeight rounded-lg" type="text" placeholder="请填写开户行">
|
||||
</view>
|
||||
<view class="text-center text-txGray text-md mt-60rpx">提示:请认真填写,避免发生错误!</view>
|
||||
<view class="mt-50rpx">
|
||||
<view class="login-btn" @tap="onSubmit"> 保存 </view>
|
||||
</view>
|
||||
</view>
|
||||
<u-select @confirm="BankConfirm" label-name="name" v-model="bankShow" confirm-color="#378264" :list="banks"></u-select>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isFirstLoading:true,
|
||||
bankShow:false,
|
||||
bankList:[],
|
||||
real_name:'',//真实姓名,
|
||||
bank_number:'',//卡号,
|
||||
bank_name:'',//银行名称
|
||||
bank_description:'',//开户行
|
||||
is_edited:false
|
||||
};
|
||||
},
|
||||
computed:{
|
||||
banks(){
|
||||
const arr=[]
|
||||
this.bankList.forEach((item,index)=>{
|
||||
let obj={}
|
||||
obj.name=item
|
||||
arr.push(obj)
|
||||
})
|
||||
return arr
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.getDate()
|
||||
this.getBank()
|
||||
},
|
||||
methods:{
|
||||
//查询银行卡信息
|
||||
async getDate(){
|
||||
let resDate=await this.$api.get('/v1/user-bank',{})
|
||||
if(resDate.real_name){
|
||||
this.real_name=resDate.real_name,
|
||||
this.bank_number=resDate.bank_number,
|
||||
this.bank_name=resDate.bank_name,
|
||||
this.bank_description=resDate.bank_description,
|
||||
this.is_edited=resDate.is_edited
|
||||
}
|
||||
this.isFirstLoading = false
|
||||
},
|
||||
//获取银行信息
|
||||
async getBank(){
|
||||
let resDate=await this.$api.get('/v1/banks-options',{})
|
||||
this.bankList=resDate.banks
|
||||
},
|
||||
//选择的银行
|
||||
BankConfirm(e){
|
||||
this.bank_name=e[0].label
|
||||
},
|
||||
async onSubmit(){
|
||||
let obj={
|
||||
real_name:this.real_name,
|
||||
bank_number:this.bank_number,
|
||||
bank_name:this.bank_name,
|
||||
bank_description:this.bank_description
|
||||
}
|
||||
if(!obj.real_name)return this.$u.toast("请填写持卡人姓名")
|
||||
if(!this.$u.test.rangeLength(obj.bank_number, [16, 25]))return this.$u.toast("银行卡格式不正确")
|
||||
if(!obj.bank_name)return this.$u.toast("请选择所属银行")
|
||||
if(!obj.bank_description)return this.$u.toast("请填写开户行")
|
||||
try{
|
||||
let resDate=await this.$api.put('/v1/user-bank',obj,{custom: {loading: true}})
|
||||
this.$u.toast("提交成功")
|
||||
setTimeout(()=>{
|
||||
uni.navigateBack()
|
||||
this.eliminate()
|
||||
},300)
|
||||
}catch(err){}
|
||||
},
|
||||
//清除数据
|
||||
eliminate(){
|
||||
this.real_name='',
|
||||
this.bank_number='',
|
||||
this.bank_name='',
|
||||
this.bank_description=''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
page{
|
||||
height: 100vh;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.inputHeight{
|
||||
@apply h-80rpx flex-1 border border-txBorder px-base ml-40rpx text-lg ;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
<template>
|
||||
<view>
|
||||
<!-- 导航栏 -->
|
||||
<u-navbar :background="{ background: '#f5f5f5' }" back-icon-color="#000000" title-color="#000000"
|
||||
:border-bottom="false" :title="type?'编辑地址':'新增地址'">
|
||||
<view @tap="delshow=true" v-if="type" class="pl-base text-lg ">
|
||||
删除
|
||||
</view>
|
||||
</u-navbar>
|
||||
<view class="w-full bg-white box-shadow p-base ">
|
||||
<u-form :label-style="labelstyle" :model="form" ref="uForm" :error-type="['toast']">
|
||||
<u-form-item prop="consignee" label="收货人" label-width="220">
|
||||
|
||||
<input placeholder-class="placeholder" :adjust-position="false" class="w-full h-full" type="text"
|
||||
v-model="form.consignee" placeholder="请填写收件人名称">
|
||||
</u-form-item>
|
||||
<u-form-item prop="telephone" label="电话" label-width="220">
|
||||
<input maxlength="11" placeholder-class="placeholder" :adjust-position="false" class="w-full h-full" type="number"
|
||||
v-model="form.telephone" placeholder="请填写联系方式">
|
||||
|
||||
</u-form-item>
|
||||
<u-form-item prop="zone" label="所在地区" label-width="220">
|
||||
<view class="flex items-center justify-between w-full">
|
||||
<view @tap="show=true" class="flex-1 text-md" :class="form.zone?'':'text-hex-c0c4cc'">
|
||||
{{form.zone?form.zone:'请选择地区'}}
|
||||
</view>
|
||||
<!-- <u-input :adjust-position="false" @tap="show=true" :clearable="false" v-model="form.zone"
|
||||
placeholder="请选择地区" disabled/> -->
|
||||
<view @tap="getJudgeMap" class="flex items-center text-md text-txBase">
|
||||
<view>定位</view>
|
||||
<u-icon name="map" color="#000000" size="36"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 弹窗 -->
|
||||
<u-select :default-value="defaultValue" value-name="id" label-name="name" mode="mutil-column-auto"
|
||||
@confirm="regConfirm" confirm-color="#378264" v-model="show" :list="regList"></u-select>
|
||||
</u-form-item>
|
||||
<u-form-item prop="address" label="详细地址" label-width="220">
|
||||
<input placeholder-class="placeholder" :adjust-position="false" class="w-full h-full" type="text"
|
||||
v-model="form.address" placeholder="请填写详细地址">
|
||||
<!-- <u-input :adjust-position="false" :clearable="false" v-model="form.address" placeholder="请填写详细地址" /> -->
|
||||
</u-form-item>
|
||||
<u-form-item label="设为默认地址" label-width="220" :border-bottom="false">
|
||||
<u-switch inactive-color="#808080" active-color="#378264" slot="right" v-model="form.is_default"></u-switch>
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
<view class=" text-txGray text-md mt-46rpx px-base">提示:请认真填写收货地址,避免寄送发生错误</view>
|
||||
<view class="mt-200rpx px-base">
|
||||
<view class="login-btn" @tap="onSubmit"> 保存地址 </view>
|
||||
</view>
|
||||
<cu-modal v-model="delshow" @confirm="confirm" confirm-color="#378264" show-cancel-button
|
||||
content="是否确认删除"></cu-modal>
|
||||
<cu-modal title="提示" v-model="showModel" content="没有开启定位权限,点击确定授权" confirm-color="#378264" show-cancel-button
|
||||
@confirm="onConfirm"></cu-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getTreeData } from '@/utils'
|
||||
import addressParse from '@/utils/address_parse'
|
||||
export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
showModel: false,
|
||||
form: {
|
||||
consignee: '', //收件人
|
||||
telephone: '', //电话
|
||||
zone_id: '', //地区id
|
||||
zone: '', //所在地区文字
|
||||
address: '', //详细地址
|
||||
is_default: false, //是否默认
|
||||
id: '',
|
||||
},
|
||||
rules: {
|
||||
consignee: [{
|
||||
required: true,
|
||||
message: '请填写收件人',
|
||||
trigger: 'change',
|
||||
}, ],
|
||||
telephone: [{
|
||||
required: true,
|
||||
message: '请输入手机号',
|
||||
trigger: 'change',
|
||||
},
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
return this.$u.test.mobile(value);
|
||||
},
|
||||
message: '手机号码不正确',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
zone: [{
|
||||
required: true,
|
||||
message: '请选择所在地',
|
||||
trigger: 'blur',
|
||||
}, ],
|
||||
address: [{
|
||||
required: true,
|
||||
message: '请填写详细地址',
|
||||
trigger: 'change',
|
||||
}, ],
|
||||
},
|
||||
|
||||
show: false,
|
||||
labelstyle: {
|
||||
fontSize: '30rpx',
|
||||
color: '#383838'
|
||||
},
|
||||
regList: [], //地区数据
|
||||
defaultValue: [0, 0, 0], //地区默认选中
|
||||
type: false, //判断是编辑地址还是新增地址
|
||||
delshow:false,
|
||||
addressType:''
|
||||
};
|
||||
},
|
||||
onReady() {
|
||||
this.$refs.uForm.setRules(this.rules);
|
||||
},
|
||||
async onLoad({
|
||||
info,
|
||||
addressType
|
||||
}) {
|
||||
if (info) {
|
||||
this.type = true
|
||||
let item = JSON.parse(info)
|
||||
this.form.consignee = item.consignee
|
||||
this.form.is_default = item.is_default
|
||||
this.form.telephone = item.telephone
|
||||
this.form.zone = item.zone
|
||||
this.form.zone_id = item.zone_id
|
||||
this.form.address = item.address
|
||||
this.form.id = item.id
|
||||
}
|
||||
this.addressType = addressType
|
||||
await this.getRegion()
|
||||
},
|
||||
methods: {
|
||||
onMap(){
|
||||
uni.chooseLocation({
|
||||
success: (res) => {
|
||||
// console.log(res);
|
||||
if(res.name=='') return
|
||||
const addr = addressParse(res.address,this.regList)
|
||||
// console.log(addr);
|
||||
const locat = [{
|
||||
value:addr.provinceCode,
|
||||
label:addr.province
|
||||
},{
|
||||
value:addr.cityCode,
|
||||
label:addr.city
|
||||
},{
|
||||
value:addr.areaCode,
|
||||
label:addr.area
|
||||
}].filter(({value})=>!!value)
|
||||
this.defaultValue = this.treeFindPath(this.regList,node => node.id === locat[locat.length-1].value)
|
||||
this.form.zone_id = locat[locat.length-1].value
|
||||
this.form.zone = locat.map(({label})=>label).join(' ')
|
||||
this.form.address = addr.address
|
||||
}
|
||||
});
|
||||
},
|
||||
// 开始
|
||||
//判断小程序定位权限是否开启
|
||||
getJudgeMap(){
|
||||
uni.authorize({
|
||||
scope: 'scope.userLocation',
|
||||
success: async res => {
|
||||
await this.onMap()
|
||||
},
|
||||
fail: (comp) => {
|
||||
uni.getSetting({
|
||||
success: Setting => {
|
||||
if (!Setting.authSetting['scope.userLocation']) {
|
||||
this.showModel = true
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
onConfirm() {
|
||||
this.showModel = false
|
||||
uni.openSetting({
|
||||
success: res => {
|
||||
},
|
||||
});
|
||||
},
|
||||
//结束
|
||||
//获取地区
|
||||
async getRegion() {
|
||||
let res = await this.$api.get('/v1/zones');
|
||||
this.regList = getTreeData(res, null, 'parent_id', 'id', 'children', 'code')
|
||||
this.defaultValue = this.treeFindPath(this.regList,node => node.id === this.form.zone_id)
|
||||
},
|
||||
//选择的地区
|
||||
regConfirm(e) {
|
||||
this.form.zone_id = e[2].value
|
||||
this.form.zone = e[0].label + ' ' + e[1].label + ' ' + e[2].label
|
||||
this.defaultValue = this.treeFindPath(this.regList,node => node.id === e[2].value)
|
||||
},
|
||||
treeFindPath (tree, func, path = []) {
|
||||
if (!tree) return []
|
||||
let i = 0
|
||||
for (const data of tree) {
|
||||
|
||||
|
||||
path.push(i)
|
||||
if (func(data)) return path
|
||||
i++
|
||||
if (data.children) {
|
||||
const findChildren = this.treeFindPath(data.children, func, path)
|
||||
if (findChildren.length) return findChildren
|
||||
}
|
||||
path.pop()
|
||||
}
|
||||
return []
|
||||
},
|
||||
|
||||
confirm(){
|
||||
try{
|
||||
this.$api.delete(`/v1/shipping-addresses/${this.form.id}`, {}, {
|
||||
custom: {
|
||||
loading: true
|
||||
},
|
||||
});
|
||||
this.$u.toast("删除成功")
|
||||
this.type = false
|
||||
uni.navigateBack()
|
||||
}catch(err){}
|
||||
},
|
||||
//提交
|
||||
onSubmit() {
|
||||
this.$refs.uForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
let obj = {
|
||||
consignee: this.form.consignee,
|
||||
telephone: this.form.telephone,
|
||||
zone_id: this.form.zone_id,
|
||||
address: this.form.address,
|
||||
is_default: this.form.is_default
|
||||
}
|
||||
//新增
|
||||
if (!this.type) {
|
||||
let res = await this.$api.post('/v1/shipping-addresses', obj, {
|
||||
custom: {
|
||||
loading: true
|
||||
},
|
||||
});
|
||||
if(this.addressType=='select') uni.$emit('address:select', res)
|
||||
|
||||
} else {
|
||||
//修改
|
||||
let res = await this.$api.put(`/v1/shipping-addresses/${this.form.id}`, obj, {
|
||||
custom: {
|
||||
loading: true
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
this.$u.toast("保存成功")
|
||||
this.$refs.uForm.resetFields()
|
||||
this.type = false
|
||||
uni.navigateBack()
|
||||
} catch (error) {
|
||||
// console.log(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.box-shadow {
|
||||
box-shadow: 0px 2rpx 4rpx rgba(0, 0, 0, 0.13);
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
<template>
|
||||
<view>
|
||||
<!-- 导航栏 -->
|
||||
<u-navbar back-icon-color="#ffffff" :border-bottom="false" title-color="#ffffff" :background="{ background: '#378264' }">
|
||||
<view class="flex-1 text-center text-white text-32rpx">消息</view>
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<view slot="right" class="pr-base">
|
||||
<u-icon name="kefu-ermai" color="#ffffff" size="48" @tap="$u.route(`/pages/web_view/index?url=${service}`)"></u-icon>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button slot="right" class="pr-base cu-btn" open-type="contact" hover-class="none">
|
||||
<u-icon name="kefu-ermai" color="#ffffff" size="48" ></u-icon>
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
</u-navbar>
|
||||
<mescroll-body :height="height" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
|
||||
<block v-for="(item, index) in dataList" :key="index">
|
||||
<!-- 订单消息 -->
|
||||
<block v-if="item.type == 1">
|
||||
<view @tap="jump(item.jump_type, item.jump_link)" class="w-710rpx bg-white rounded-xs m-auto mt-base text-lg text-txBase">
|
||||
<view class="p-base border-b border-txBorder">{{ item.title }}</view>
|
||||
<view class="px-base pt-base">{{ item.content }}</view>
|
||||
<!-- <view class="px-base">合计:99.99元</view> -->
|
||||
<view class="px-base">姓名:{{ item.ext.consignee_name }}</view>
|
||||
<view class="px-base">电话:{{ item.ext.consignee_telephone }}</view>
|
||||
<view class="px-base">地址:{{ item.ext.consignee_address }}</view>
|
||||
<view class="flex mt-base items-center justify-between p-base border-t border-txBorder">
|
||||
<view v-if="item.has_read">已确认</view>
|
||||
<view @tap.stop="confirm(item.id)" v-else class="w-180rpx h-66rpx leading-66rpx bg-bgSubtitle text-white text-center rounded-lg"
|
||||
>确认
|
||||
</view>
|
||||
|
||||
<view class="text-txGray">{{ item.created_at }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 公告 -->
|
||||
<block v-if="item.type == 0">
|
||||
<view @tap="jump(item.jump_type, item.jump_link)" class="w-710rpx bg-white rounded-xs m-auto mt-base text-lg text-txBase">
|
||||
<view class="p-base border-b border-txBorder">{{ item.title }}</view>
|
||||
<view class="p-base">{{ item.content }}</view>
|
||||
<view class="text-txGray p-base text-right border-t border-txBorder">{{ item.created_at }}</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
|
||||
export default {
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
// service:process.env.VUE_APP_SERVICE,
|
||||
downOption: {
|
||||
auto: true,
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20,
|
||||
},
|
||||
noMoreSize: 1,
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
};
|
||||
},
|
||||
onShow() {
|
||||
this.getRead();
|
||||
},
|
||||
computed: {
|
||||
service(){
|
||||
const clien = process.env.VUE_APP_SERVICE
|
||||
const user = this.$store.getters.user ?? {}
|
||||
const phone = user?.phone
|
||||
const name = user?.nickname ?? phone ?? '访客'
|
||||
return `${clien}?u_cust_name=${name}&u_cust_phone=${phone}&u_cust_id=${phone}`
|
||||
},
|
||||
height() {
|
||||
const { windowHeight, statusBarHeight } = this.$u.sys();
|
||||
return windowHeight - statusBarHeight - 44 + 'px';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
downCallback() {
|
||||
this.dataList = [];
|
||||
this.mescroll.resetUpScroll();
|
||||
},
|
||||
async upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData(page) {
|
||||
this.$api
|
||||
.get(`/v1/messages`, {
|
||||
params: {
|
||||
page: page.num,
|
||||
per_page: page.size,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
this.mescroll.endSuccess(res.data.length);
|
||||
if (page.num == 1) this.dataList = [];
|
||||
this.dataList = this.dataList.concat(res.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
this.mescroll.endErr();
|
||||
});
|
||||
},
|
||||
//一键消息已读
|
||||
async getRead() {
|
||||
await this.$api.post('/v1/messages/batch-read');
|
||||
await this.$store.dispatch('user/getNewsNum');
|
||||
},
|
||||
//确认消息
|
||||
async confirm(id) {
|
||||
try {
|
||||
let resDate = await this.$api.post(
|
||||
`/v1/messages/read/${id}`,
|
||||
{},
|
||||
{
|
||||
custom: {
|
||||
loading: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
let Index = this.dataList.findIndex((item) => {
|
||||
return item.id == id;
|
||||
});
|
||||
this.dataList[Index].has_read = true;
|
||||
await this.$store.dispatch('user/getNewsNum');
|
||||
} catch (err) {}
|
||||
},
|
||||
//跳转页面
|
||||
jump(type, url) {
|
||||
if (type == 1 && url) {
|
||||
uni.navigateTo({
|
||||
url: url,
|
||||
});
|
||||
} else if (type == 2 && url) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/web_view/index?url=' + url,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bottom-bg {
|
||||
width: 100%;
|
||||
height: 300rpx;
|
||||
text-align: center;
|
||||
background: $u-type-primary;
|
||||
clip-path: ellipse(140% 100% at 50% 0%);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
<template>
|
||||
<!-- 个人信息 -->
|
||||
<view>
|
||||
<nav-shadow></nav-shadow>
|
||||
<view class="bg-white px-base py-base flex items-center justify-between border-b border-txBorder" @tap="uploadImge">
|
||||
<view class="text-md text-txBase">头像</view>
|
||||
<view class="flex items-center">
|
||||
<cu-avatar size="120" :src="user.avatar ? user.avatar : ''"></cu-avatar>
|
||||
<!-- <u-avatar size="108"
|
||||
:src="user.avatar?user.avatar:'http://pic2.sc.chinaz.com/Files/pic/pic9/202002/hpic2119_s.jpg'"></u-avatar> -->
|
||||
<u-icon class="ml-10rpx" color="#383838" name="arrow-right" size="32"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white px-base py-30rpx flex items-center justify-between border-b border-txBorder">
|
||||
<view class="text-md text-txBase">手机号</view>
|
||||
<view>{{ user.phone }}</view>
|
||||
</view>
|
||||
<view class="bg-white px-base py-30rpx flex items-center justify-between border-b border-txBorder">
|
||||
<view class="text-md text-txBase">昵称</view>
|
||||
<input class="text-md text-txBase text-right" v-model="user.nickname" type="text" placeholder="请输入昵称" />
|
||||
</view>
|
||||
<view @tap="sexShow = true" class="bg-white px-base py-30rpx flex items-center justify-between border-b border-txBorder">
|
||||
<view class="text-md text-txBase">性别</view>
|
||||
<view class="flex items-center">
|
||||
<view class="text-md text-txBase">{{ user.gender | filterGender }}</view>
|
||||
<u-icon class="text-md" color="#383838" name="arrow-right" size="32"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view @tap="dateShow = true" class="bg-white px-base py-30rpx flex items-center justify-between">
|
||||
<view class="text-md text-txBase">出生日期</view>
|
||||
<view class="flex items-center">
|
||||
<view v-if="user.birthday" class="text-md text-txBase">{{ user.birthday }}</view>
|
||||
<view v-else class="text-md text-txGray">请选择出生日期</view>
|
||||
<u-icon class="ml-10rpx" color="#383838" name="arrow-right" size="32"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="login-btn mx-base mt-80rpx" @tap="onSubmit">保存</view>
|
||||
<!-- 选择性别弹窗 -->
|
||||
<cu-select v-model="sexShow" :list="sexList" @confirm="sexConfirm" confirm-color="#378264"></cu-select>
|
||||
<!-- 出生日期弹窗 -->
|
||||
<u-picker v-model="dateShow" mode="time" @confirm="dateConfirm" confirm-color="#378264"></u-picker>
|
||||
<!-- 上传头像 -->
|
||||
<!-- <upload v-model="show" @result="result"></upload> -->
|
||||
<!-- 是否放弃修改弹窗 -->
|
||||
<cu-modal v-model="upShow" @confirm="canceConfirm" confirm-color="#378264" show-cancel-button content="是否放弃本次修改"> </cu-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uploadImage from '@/utils/ossutil/uploadFile.js';
|
||||
import { mapGetters } from 'vuex';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
sexShow: false,
|
||||
sexList: [
|
||||
{
|
||||
value: 'unknown',
|
||||
label: '未知',
|
||||
},
|
||||
{
|
||||
value: 'male',
|
||||
label: '男',
|
||||
},
|
||||
{
|
||||
value: 'female',
|
||||
label: '女',
|
||||
},
|
||||
],
|
||||
dateShow: false,
|
||||
upShow: false,
|
||||
user: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
userInfo: 'user',
|
||||
}),
|
||||
},
|
||||
watch: {
|
||||
userInfo: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(e) {
|
||||
this.user =Object.assign({}, e);
|
||||
},
|
||||
},
|
||||
},
|
||||
onLoad(){
|
||||
this.$store.dispatch('user/getUserInfo')
|
||||
},
|
||||
onBackPress() {
|
||||
if (this.show || this.sexShow || this.dateShow) {
|
||||
this.show = false;
|
||||
this.sexShow = false;
|
||||
this.dateShow = false;
|
||||
this.upShow = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//上传头像
|
||||
uploadImge(){
|
||||
console.log(2222)
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
crop:{
|
||||
width:'80',
|
||||
height:'80',
|
||||
},
|
||||
success: (res) => {
|
||||
this.user.avatar = res.tempFilePaths[0];
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
},
|
||||
canceConfirm() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
result(e) {
|
||||
this.user.avatar = e;
|
||||
},
|
||||
//性别选择
|
||||
sexConfirm(e) {
|
||||
this.user.gender = e[0].value;
|
||||
},
|
||||
//日期选择
|
||||
dateConfirm(e) {
|
||||
this.user.birthday = `${e.year}-${e.month}-${e.day}`;
|
||||
},
|
||||
async uploads() {
|
||||
const arr = [];
|
||||
try {
|
||||
const resData = await this.$api.post('/v1/oss-sts');
|
||||
// const host = `https://${resData.bucket}.${resData.region_id}.aliyuncs.com/`;
|
||||
const host = `https://${resData.domain}/`;
|
||||
var upFile = new Promise((r) => {
|
||||
uploadImage(
|
||||
this.user.avatar,
|
||||
{
|
||||
uploadImageUrl: host,
|
||||
AccessKeySecret: resData.AccessKeySecret,
|
||||
OSSAccessKeyId: resData.AccessKeyId,
|
||||
stsToken: resData.SecurityToken,
|
||||
timeout: 3600,
|
||||
dir: 'app/avatar/',
|
||||
},
|
||||
(result) => {
|
||||
console.log(result);
|
||||
this.user.avatar = result;
|
||||
r();
|
||||
console.log(result);
|
||||
},
|
||||
(err) => {
|
||||
console.log(err);
|
||||
},
|
||||
);
|
||||
});
|
||||
arr.push(upFile);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
this.$u.toast(err.message || err.errMsg);
|
||||
}
|
||||
return Promise.all(arr);
|
||||
},
|
||||
//提交
|
||||
async onSubmit() {
|
||||
if (!this.user.birthday) {
|
||||
return this.$u.toast('请选择出生日期');
|
||||
}
|
||||
if (this.user.avatar != this.$store.getters.user.avatar) {
|
||||
await this.uploads();
|
||||
}
|
||||
let val = this.user;
|
||||
let obj = {
|
||||
birthday: val.birthday,
|
||||
};
|
||||
if (val.nickname) {
|
||||
obj.nickname = val.nickname;
|
||||
}
|
||||
if (val.avatar) {
|
||||
obj.avatar = val.avatar;
|
||||
}
|
||||
if (val.gender) {
|
||||
obj.gender = val.gender;
|
||||
}
|
||||
let res = await this.$api.put('/v1/me', obj, {
|
||||
custom: {
|
||||
loading: true,
|
||||
},
|
||||
});
|
||||
this.$u.toast('修改成功');
|
||||
this.$store.dispatch('user/getUserInfo');
|
||||
let info = {
|
||||
phone: this.user.phone,
|
||||
avatar: this.user.avatar,
|
||||
nickname: this.user.nickname,
|
||||
};
|
||||
this.$store.commit('user/SET_REFRESH', info);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<view class="px-base">
|
||||
<nav-shadow></nav-shadow>
|
||||
<view class="flex py-20rpx leading-48rpx text-md" @tap="$u.route('/pageA/reset_password/login')">
|
||||
<view class="flex-1">账号密码</view>
|
||||
<u-icon name="arrow-right"></u-icon>
|
||||
</view>
|
||||
<u-line></u-line>
|
||||
<view class="flex py-20rpx leading-48rpx text-md" @tap="$u.route('/pageA/reset_password/reset_security')">
|
||||
<view class="flex-1">安全密码</view>
|
||||
<u-icon name="arrow-right"></u-icon>
|
||||
</view>
|
||||
<u-line></u-line>
|
||||
</view>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: #FFFFFF !important;
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
<template>
|
||||
<view class="">
|
||||
<nav-shadow></nav-shadow>
|
||||
<view class="px-45rpx pt-20rpx">
|
||||
<cu-form :model="form" ref="uForm" :error-type="['toast']">
|
||||
<u-form-item prop="password">
|
||||
<view class="flex items-center w-full">
|
||||
<view class="w-60rpx flex items-center">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/user/pwd-icon.png" mode="scaleToFill" />
|
||||
</view>
|
||||
<u-input type="password" :clearable="false" v-model="form.password" class="w-full" placeholder="新密码" />
|
||||
</view>
|
||||
</u-form-item>
|
||||
<u-form-item prop="confirm_password" class="mt-20rpx">
|
||||
<view class="flex items-center w-full">
|
||||
<view class="w-60rpx flex items-center">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/user/pwd-icon.png" mode="scaleToFill" />
|
||||
</view>
|
||||
<u-input type="password" :clearable="false" v-model="form.confirm_password" class="w-full"
|
||||
placeholder="确认密码" />
|
||||
</view>
|
||||
</u-form-item>
|
||||
<u-form-item prop="phone" class="mt-20rpx">
|
||||
<view class="flex items-center">
|
||||
<view class="w-60rpx flex items-center">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/user/phone-icon.png" mode="scaleToFill" />
|
||||
</view>
|
||||
<u-input :clearable="false" v-model="form.phone" class="w-full" placeholder="手机号" />
|
||||
</view>
|
||||
</u-form-item>
|
||||
<u-form-item prop="verify_code" class="mt-20rpx">
|
||||
<view class="flex items-center w-full">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/user/code-icon.png" />
|
||||
<sms-input class="w-full" ref="smsRef" v-model="form.verify_code" :maxLength="6" @run="getCode"></sms-input>
|
||||
</view>
|
||||
</u-form-item>
|
||||
</cu-form>
|
||||
|
||||
<view class="mt-70rpx">
|
||||
<view class="login-btn" @tap="onSubmit">
|
||||
{{ButtonTitle}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tips: '',
|
||||
type: '',
|
||||
form: {
|
||||
phone: '',
|
||||
password: '',
|
||||
confirm_password: '',
|
||||
verify_code: ''
|
||||
},
|
||||
rules: {
|
||||
phone: [{
|
||||
required: true,
|
||||
message: '请输入手机号',
|
||||
trigger: 'change',
|
||||
},
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
return this.$u.test.mobile(value);
|
||||
},
|
||||
message: '手机号码不正确',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
confirm_password: [{
|
||||
required: true,
|
||||
message: '请再次输入密码',
|
||||
trigger: 'change',
|
||||
},
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
return value == this.form.password
|
||||
},
|
||||
message: '两次密码不一致',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
password: [{
|
||||
required: true,
|
||||
message: '请输入密码',
|
||||
trigger: 'change',
|
||||
}, ],
|
||||
verify_code: [{
|
||||
required: true,
|
||||
message: '请输入验证码',
|
||||
trigger: 'change',
|
||||
}, ],
|
||||
},
|
||||
};
|
||||
},
|
||||
onLoad({
|
||||
type
|
||||
}) {
|
||||
// 动态设置导航栏标题
|
||||
if (type) {
|
||||
this.type = type
|
||||
uni.setNavigationBarTitle({
|
||||
title: '找回密码'
|
||||
});
|
||||
}
|
||||
},
|
||||
onReady() {
|
||||
this.$refs.uForm.setRules(this.rules);
|
||||
},
|
||||
computed: {
|
||||
ButtonTitle() {
|
||||
return this.type ? '提交' : '保存修改'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//发送短信验证码
|
||||
async getCode() {
|
||||
this.$refs.uForm.validateField(['phone'], async (error) => {
|
||||
if (error.length == 0) {
|
||||
try {
|
||||
await this.$api.post('/v1/sms-codes', {
|
||||
phone: this.form.phone,
|
||||
type: '2'
|
||||
}, {
|
||||
custom: {
|
||||
loading: true
|
||||
}
|
||||
});
|
||||
this.$refs.smsRef.start();
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
onSubmit() {
|
||||
this.$refs.uForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
let obj = {
|
||||
phone: this.form.phone,
|
||||
password: this.form.password,
|
||||
verify_code: this.form.verify_code,
|
||||
}
|
||||
|
||||
try {
|
||||
let res = await this.$api.post('/v1/reset-password', obj, {
|
||||
custom: {
|
||||
loading: true
|
||||
}
|
||||
});
|
||||
// this.$refs.uCode.start();
|
||||
uni.showToast({
|
||||
title: '修改成功',
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
// this.$u.route({
|
||||
// type: 'redirectTo',
|
||||
// url: '/pages/login/index'
|
||||
// })
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
})
|
||||
}, 1000)
|
||||
this.$refs.uForm.resetFields()
|
||||
|
||||
} catch (error) {
|
||||
// console.log(error);
|
||||
} finally {}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
<template>
|
||||
<view class="">
|
||||
<nav-shadow></nav-shadow>
|
||||
<view class="px-45rpx pt-20rpx">
|
||||
<cu-form :model="form" ref="uForm" :error-type="['toast']">
|
||||
<u-form-item prop="phone" class="mt-20rpx">
|
||||
<view class="flex items-center">
|
||||
<view class="w-60rpx flex items-center">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/user/phone-icon.png" mode="scaleToFill" />
|
||||
</view>
|
||||
<u-input type="number" disabled :clearable="false" v-model="user.phone" class="w-full" placeholder="手机号" />
|
||||
</view>
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item prop="verify_code" class="mt-20rpx">
|
||||
<view class="flex items-center w-full">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/user/code-icon.png" />
|
||||
<sms-input class="w-full" ref="smsRef" v-model="form.verify_code" :maxLength="6" @run="getCode"></sms-input>
|
||||
</view>
|
||||
</u-form-item>
|
||||
<u-form-item prop="password" class="mt-20rpx">
|
||||
<view class="flex items-center w-full">
|
||||
<view class="w-60rpx flex items-center">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/user/pwd-icon.png" mode="scaleToFill" />
|
||||
</view>
|
||||
<!-- -->
|
||||
<!-- 还未输入安全密码时 -->
|
||||
<view v-if="!form.password" @tap="popupShow=!popupShow" class="flex-1 text-md text-hex-c0c4cc" > 请输入安全密码</view>
|
||||
<!-- 输入安全密码时 -->
|
||||
<view v-else @tap="popupShow=!popupShow" class="flex-1 text-md">
|
||||
<text v-for="(item,index) in form.password" :key="index">{{!eyeShow?'●':item}}</text>
|
||||
</view>
|
||||
<!-- -->
|
||||
<!-- <u-input disabled :password-icon="false" @tap="popupShow=!popupShow" :type="!eyeShow?'password':'text'"
|
||||
:clearable="false" v-model="form.password" class="w-full" placeholder="请输入安全密码" /> -->
|
||||
<view @tap="eyeShow=!eyeShow">
|
||||
<u-icon size="32" color="#c0c4cc" :name="eyeShow?'eye':'eye-fill'"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</u-form-item>
|
||||
</cu-form>
|
||||
<view class="mt-110rpx">
|
||||
<view @tap="onSubmit"
|
||||
class="login-btn">
|
||||
确认
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 打开键盘 -->
|
||||
<u-keyboard @change="onChange" @backspace="backspace" :mask="false" :show-tips="false" :dot-enabled="false"
|
||||
ref="uKeyboard" mode="number" v-model="popupShow"></u-keyboard>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
eyeShow: false,
|
||||
popupShow: false,
|
||||
form: {
|
||||
password: '',
|
||||
verify_code: ''
|
||||
},
|
||||
rules: {
|
||||
password: [{
|
||||
required: true,
|
||||
message: '请输入安全密码',
|
||||
trigger: 'change',
|
||||
},
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
return this.$u.test.rangeLength(value, [6, 6])
|
||||
},
|
||||
message: '安全密码长度为6位数',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
verify_code: [{
|
||||
required: true,
|
||||
message: '请输入验证码',
|
||||
trigger: 'change',
|
||||
}, ],
|
||||
},
|
||||
};
|
||||
},
|
||||
onReady() {
|
||||
this.$refs.uForm.setRules(this.rules);
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.getters.user ?? {};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
//发送短信验证码
|
||||
async getCode() {
|
||||
await this.$api.post(
|
||||
'/v1/sms-codes', {
|
||||
phone: this.user.phone,
|
||||
type: '3',
|
||||
}, {
|
||||
custom: {
|
||||
loading: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
this.$refs.smsRef.start();
|
||||
},
|
||||
// 按键被点击(点击退格键不会触发此事件)
|
||||
onChange(e) {
|
||||
this.form.password += e
|
||||
if (this.$u.test.code(this.form.password, 6)) {
|
||||
this.popupShow = false
|
||||
}
|
||||
},
|
||||
// 退格键被点击
|
||||
backspace() {
|
||||
if (this.form.password.length) this.form.password = this.form.password.substr(0, this.form.password.length - 1);
|
||||
},
|
||||
onSubmit() {
|
||||
this.$refs.uForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '保存中',
|
||||
mask: true
|
||||
});
|
||||
let obj={
|
||||
verify_code:this.form.verify_code,
|
||||
password:this.form.password
|
||||
}
|
||||
let resDate = await this.$api.put('/v1/wallet-password', obj)
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
this.$u.toast('修改成功');
|
||||
setTimeout(() => {
|
||||
uni.navigateBack({})
|
||||
}, 300)
|
||||
}, 300)
|
||||
this.$store.dispatch('user/getUserInfo');
|
||||
|
||||
} catch (err) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
<template>
|
||||
<view class="">
|
||||
<nav-shadow></nav-shadow>
|
||||
<view class="px-45rpx pt-20rpx">
|
||||
<cu-form :model="form" ref="uForm" :error-type="['toast']">
|
||||
<u-form-item prop="phone" class="mt-20rpx">
|
||||
<view class="flex items-center">
|
||||
<view class="w-60rpx flex items-center">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/user/phone-icon.png" mode="scaleToFill" />
|
||||
</view>
|
||||
<u-input type="number" disabled :clearable="false" v-model="user.phone" class="w-full" placeholder="手机号" />
|
||||
</view>
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item prop="verify_code" class="mt-20rpx">
|
||||
<view class="flex items-center w-full">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/user/code-icon.png" />
|
||||
<sms-input class="w-full" ref="smsRef" v-model="form.verify_code" :maxLength="6" @run="getCode"></sms-input>
|
||||
</view>
|
||||
</u-form-item>
|
||||
<u-form-item prop="password" class="mt-20rpx">
|
||||
<view class="flex items-center w-full">
|
||||
<view class="w-60rpx flex items-center">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/user/pwd-icon.png" mode="scaleToFill" />
|
||||
</view>
|
||||
<!-- -->
|
||||
<!-- 还未输入安全密码时 -->
|
||||
<view v-if="!form.password" @tap="popupShow=!popupShow" class="flex-1 text-md text-hex-c0c4cc" > 请输入安全密码</view>
|
||||
<!-- 输入安全密码时 -->
|
||||
<view v-else @tap="popupShow=!popupShow" class="flex-1 text-md">
|
||||
<text v-for="(item,index) in form.password" :key="index">{{!eyeShow?'●':item}}</text>
|
||||
</view>
|
||||
<!-- -->
|
||||
<!-- <u-input disabled :password-icon="false" @tap="popupShow=!popupShow" :type="!eyeShow?'password':'text'"
|
||||
:clearable="false" v-model="form.password" class="w-full" placeholder="请输入安全密码" /> -->
|
||||
<view @tap="eyeShow=!eyeShow">
|
||||
<u-icon size="32" color="#c0c4cc" :name="eyeShow?'eye':'eye-fill'"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</u-form-item>
|
||||
</cu-form>
|
||||
<view class="mt-110rpx">
|
||||
<view @tap="onSubmit"
|
||||
class="login-btn">
|
||||
确认
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 打开键盘 -->
|
||||
<u-keyboard @change="onChange" @backspace="backspace" :mask="false" :show-tips="false" :dot-enabled="false"
|
||||
ref="uKeyboard" mode="number" v-model="popupShow"></u-keyboard>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
eyeShow: false,
|
||||
popupShow: false,
|
||||
form: {
|
||||
password: '',
|
||||
verify_code: ''
|
||||
},
|
||||
rules: {
|
||||
password: [{
|
||||
required: true,
|
||||
message: '请输入安全密码',
|
||||
trigger: 'change',
|
||||
},
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
return this.$u.test.rangeLength(value, [6, 6])
|
||||
},
|
||||
message: '安全密码长度为6位数',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
verify_code: [{
|
||||
required: true,
|
||||
message: '请输入验证码',
|
||||
trigger: 'change',
|
||||
}, ],
|
||||
},
|
||||
};
|
||||
},
|
||||
onReady() {
|
||||
this.$refs.uForm.setRules(this.rules);
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.getters.user ?? {};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
//发送短信验证码
|
||||
async getCode() {
|
||||
await this.$api.post(
|
||||
'/v1/sms-codes', {
|
||||
phone: this.user.phone,
|
||||
type: '3',
|
||||
}, {
|
||||
custom: {
|
||||
loading: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
this.$refs.smsRef.start();
|
||||
},
|
||||
// 按键被点击(点击退格键不会触发此事件)
|
||||
onChange(e) {
|
||||
this.form.password += e
|
||||
if (this.$u.test.code(this.form.password, 6)) {
|
||||
this.popupShow = false
|
||||
}
|
||||
},
|
||||
// 退格键被点击
|
||||
backspace() {
|
||||
if (this.form.password.length) this.form.password = this.form.password.substr(0, this.form.password.length - 1);
|
||||
},
|
||||
onSubmit() {
|
||||
this.$refs.uForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '保存中',
|
||||
mask: true
|
||||
});
|
||||
let obj={
|
||||
verify_code:this.form.verify_code,
|
||||
password:this.form.password
|
||||
}
|
||||
let resDate = await this.$api.put('/v1/wallet-password', obj)
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
this.$u.toast('修改成功');
|
||||
setTimeout(() => {
|
||||
uni.navigateBack({})
|
||||
}, 300)
|
||||
}, 300)
|
||||
this.$store.dispatch('user/getUserInfo');
|
||||
|
||||
} catch (err) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
<template>
|
||||
<view>
|
||||
<block v-if="bannerList.length>0">
|
||||
<cu-swiper @click="swiperJump" indicator-pos="bottomRight" mode="number" height="400" border-radius="0" :list="bannerList"></cu-swiper>
|
||||
</block>
|
||||
<!--列表 -->
|
||||
<mescroll-body :height="height" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback"
|
||||
:down="downOption" :up="upOption">
|
||||
<view class="px-rowSm mt-20rpx">
|
||||
<u-waterfall v-model="dataList" ref="uWaterfall">
|
||||
<template v-slot:left="{ leftList }">
|
||||
<view v-for="(item, index) in leftList" :key="index" class="px-rowSm mb-base">
|
||||
<goods-item :showShrough="true" :goods="item"></goods-item>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:right="{ rightList }">
|
||||
<view v-for="(item, index) in rightList" :key="index" class="px-rowSm mb-base">
|
||||
<goods-item :showShrough="true" :goods="item"></goods-item>
|
||||
</view>
|
||||
</template>
|
||||
</u-waterfall>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
export default {
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
bannerList: [],
|
||||
downOption: {
|
||||
auto: false,
|
||||
use: false
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20
|
||||
},
|
||||
noMoreSize: 1
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
height() {
|
||||
const {
|
||||
windowHeight,
|
||||
statusBarHeight
|
||||
} = this.$u.sys();
|
||||
return windowHeight - statusBarHeight - 44 + 'px';
|
||||
},
|
||||
},
|
||||
onLoad() {
|
||||
this.getBanner()
|
||||
},
|
||||
methods: {
|
||||
//轮播跳转
|
||||
swiperJump(e){
|
||||
let item=this.bannerList[e]
|
||||
this.jumpByOption(item)
|
||||
},
|
||||
jumpByOption(e) {
|
||||
if (!!e.jump_link) {
|
||||
if (e.jump_type == 1) {
|
||||
this.$u.route(e.jump_link);
|
||||
} else if (e.jump_type == 2) {
|
||||
this.$u.route(`/pages/web_view/index?url=${e.jump_link}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
//获取轮播图
|
||||
async getBanner(){
|
||||
let resDate=await this.$api.get('/v1/ads',{params: { keys: 'wechat_mini_vip_car_banner'}})
|
||||
this.bannerList=resDate.wechat_mini_vip_car_banner
|
||||
},
|
||||
downCallback() {
|
||||
this.dataList = []
|
||||
this.$refs.uWaterfall.clear();
|
||||
this.mescroll.resetUpScroll();
|
||||
},
|
||||
upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData(page) {
|
||||
let obj = {
|
||||
page: page.num,
|
||||
per_page: page.size,
|
||||
part:'vip'
|
||||
}
|
||||
this.$api.get(`/v1/product/part`, {
|
||||
params: obj
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.length)
|
||||
if (page.num == 1) this.dataList = [];
|
||||
this.dataList = this.dataList.concat(res.data);
|
||||
}).catch(err => {
|
||||
this.mescroll.endErr()
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
<template>
|
||||
<view class="bg-white px-40rpx" @tap="$u.route('/pages/order_details/index',{id:item.id})">
|
||||
<view class="h-78rpx flex justify-between items-center">
|
||||
<text class="text-lg">订单编号:{{item.sn}}</text>
|
||||
<text class="text-md text-hex-2A82E4">{{item.status |payStatusText}}</text>
|
||||
</view>
|
||||
<u-line color="#E5E5E5" />
|
||||
<block v-for="(ite,ind) in item.products" :key="ind">
|
||||
<view class="flex py-base">
|
||||
<u-image border-radius="10" width="160rpx" height="160rpx" :src="ite.cover" :lazy-load="true"></u-image>
|
||||
<view class="flex-1 ml-16rpx w-0">
|
||||
<view class="h-70rpx">
|
||||
<view class="line-2">{{ite.name}}</view>
|
||||
</view>
|
||||
<view class="text-right text-lg mt-20rpx"> x{{ite.quantity}} </view>
|
||||
</view>
|
||||
</view>
|
||||
<u-line color="#E5E5E5" />
|
||||
</block>
|
||||
<view class="flex items-center justify-between h-85rpx">
|
||||
<text class="text-md">{{item.created_date}}</text>
|
||||
<text class="text-lg font-medium">共{{total}}件 应付总额:{{price}} </text>
|
||||
</view>
|
||||
<block>
|
||||
<u-line color="#E5E5E5" />
|
||||
<view class="py-16rpx flex justify-end">
|
||||
<!-- 待付款 -->
|
||||
<!-- <view v-if="item.status==0 ||item.status==1"
|
||||
class="btn btn-primary py-10rpx px-28rpx text-md ml-base" @tap.stop="cancelOrder(item.id)">取消订单</view> -->
|
||||
<view v-if="item.status==0" class="btn btn-primary py-10rpx px-28rpx text-md ml-base"
|
||||
@tap.stop="$u.routeAuth({ url: '/pages/confirm_payment/confirm_payment', params: { id:item.id } })">立即付款
|
||||
</view>
|
||||
<!-- 待收货 -->
|
||||
<!-- <view v-if="item.status==2 ||item.status==3 || item.status==9 " class="btn btn-primary py-10rpx px-28rpx text-md ml-base" @tap.stop="$u.routeAuth({ url: '/pages/apply_refund/index', params: { id:item.id } })">去售后</view> -->
|
||||
<view v-if="item.status==3 "
|
||||
class="btn btn-primary py-10rpx px-28rpx text-md ml-base" @tap.stop="confirmReceipt(item.id)">确认收货</view>
|
||||
<!-- 已完成 -->
|
||||
<!-- <view v-if="item.status==9" class="btn btn-primary py-10rpx px-28rpx text-md ml-base "
|
||||
@tap.stop="$u.routeAuth({ url: '/pages/publish_evaluation/index', params: { id:item.id } })">去评价</view> -->
|
||||
</view>
|
||||
</block>
|
||||
<!-- 取消订单弹窗 -->
|
||||
<cu-modal v-model="cancelShow" @confirm="canceConfirm" confirm-color="#378264" show-cancel-button content="是否取消该订单" confirmText="确认取消" cancelText="再想想">
|
||||
</cu-modal>
|
||||
<!-- 确认收货弹窗 -->
|
||||
<cu-modal v-model="receiptShow" title="确认收货" @confirm="receiptConfirm" confirm-color="#378264" show-cancel-button
|
||||
content="确认收货后不能享受7天无理由退还货,是否确认收货?"></cu-modal>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cancelShow: false,
|
||||
id: '',
|
||||
receiptShow: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
price(){
|
||||
let price = ''
|
||||
if(this.item.total_amount > 0 || this.item.total_points == 0) price+=`¥${parseFloat(this.item.total_amount)}`
|
||||
if(this.item.total_amount > 0 && this.item.total_points > 0) price+= '+'
|
||||
if(this.item.total_points > 0) price+=`${this.item.total_points}积分`
|
||||
return price
|
||||
},
|
||||
total() {
|
||||
let total = 0
|
||||
this.item.products.forEach(val => {
|
||||
total += val.quantity * 1
|
||||
})
|
||||
return total
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//取消订单
|
||||
cancelOrder(id) {
|
||||
this.cancelShow = true
|
||||
this.id = id
|
||||
},
|
||||
async canceConfirm() {
|
||||
try {
|
||||
await this.$api.post(`/v1/order/orders/${this.id}/cancel`, {}, {
|
||||
custom: {
|
||||
loading: true
|
||||
},
|
||||
});
|
||||
// this.item.status = 10
|
||||
this.Parent(this.id)
|
||||
this.$u.toast(" 订单取消成功")
|
||||
} catch (err) {}
|
||||
},
|
||||
//确认收货
|
||||
confirmReceipt(id) {
|
||||
this.receiptShow = true
|
||||
this.id = id
|
||||
},
|
||||
async receiptConfirm() {
|
||||
try {
|
||||
await this.$api.post(`/v1/order/orders/${this.id}/confirm`, {}, {
|
||||
custom: {
|
||||
loading: true
|
||||
},
|
||||
});
|
||||
this.Parent(this.id)
|
||||
this.$u.toast("确认收货成功")
|
||||
} catch (err) {}
|
||||
},
|
||||
//给父元素传参,删除
|
||||
Parent(id) {
|
||||
this.$emit('Parent', id)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
<template>
|
||||
<view>
|
||||
<cu-navbar class="cu-navbar" :background="{ background: '#f5f5f5' }" :border-bottom="false">
|
||||
<view slot="right" class="flex items-center">
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<view class="px-18rpx relative" @tap="$u.route(`/pages/web_view/index?url=${service}`)">
|
||||
<u-icon name="kefu-ermai" color="#000" size="48" ></u-icon>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button class="px-18rpx relative cu-btn" open-type="contact" hover-class="none">
|
||||
<u-icon name="kefu-ermai" color="#000" size="48" ></u-icon>
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="w-full">
|
||||
<u-search @change="Change" @search='Search' border-color="#A6A6A6" placeholder="商品名称" v-model="searchText"
|
||||
:show-action="false"></u-search>
|
||||
</view>
|
||||
</cu-navbar>
|
||||
<view class="fixed top-0 w-full z-50">
|
||||
<view class="w-full" :style="[{ height: StatusBar + 44 + 'px' }]"></view>
|
||||
<u-tabs bgColor="#f5f5f5" active-color="#378264" inactive-color="#808080" bar-width="85" height="80"
|
||||
bar-height="8" :list="order" :is-scroll="false" :current="active" @change="tabChange"></u-tabs>
|
||||
</view>
|
||||
<mescroll-body top="90" :height="height" ref="mescrollRef" @init="mescrollInit" @down="downCallback"
|
||||
@up="upCallback" :down="downOption" :up="upOption">
|
||||
<view class="pt-base">
|
||||
<block v-for="(item,index) in dataList" :key="index">
|
||||
<view class="mb-24rpx">
|
||||
<order-item @Parent=Parent :item="item" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import OrderItem from './conponents/order-item.vue';
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
export default {
|
||||
mixins: [MescrollMixin],
|
||||
components: {
|
||||
OrderItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// service:process.env.VUE_APP_SERVICE,
|
||||
active: 0,
|
||||
order: [{
|
||||
name: '全部',
|
||||
key: '',
|
||||
},
|
||||
{
|
||||
name: '待付款',
|
||||
key: 'pending',
|
||||
},
|
||||
{
|
||||
name: '待收货',
|
||||
key: 'unreceived',
|
||||
},
|
||||
{
|
||||
name: '已完成',
|
||||
key: 'completed',
|
||||
}, {
|
||||
name: '已取消',
|
||||
key: 'cancelled',
|
||||
}
|
||||
],
|
||||
searchText: '',
|
||||
downOption: {
|
||||
auto: false,
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20
|
||||
},
|
||||
noMoreSize: 1
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
StatusBar() {
|
||||
const {
|
||||
statusBarHeight
|
||||
} = this.$u.sys();
|
||||
return statusBarHeight
|
||||
},
|
||||
service(){
|
||||
const clien = process.env.VUE_APP_SERVICE
|
||||
const user = this.$store.getters.user ?? {}
|
||||
const phone = user?.phone
|
||||
const name = user?.nickname ?? phone ?? '访客'
|
||||
return `${clien}?u_cust_name=${name}&u_cust_phone=${phone}&u_cust_id=${phone}`
|
||||
},
|
||||
height() {
|
||||
const {
|
||||
windowHeight,
|
||||
statusBarHeight
|
||||
} = this.$u.sys();
|
||||
return windowHeight - statusBarHeight - 44 + 'px';
|
||||
},
|
||||
},
|
||||
onLoad({
|
||||
type = 0
|
||||
}) {
|
||||
this.active = type
|
||||
// this.tabChange(type);
|
||||
},
|
||||
methods: {
|
||||
tabChange(index) {
|
||||
if (index != -1) {
|
||||
this.active = index;
|
||||
this.downCallback()
|
||||
}
|
||||
},
|
||||
downCallback() {
|
||||
this.dataList = []
|
||||
this.mescroll.resetUpScroll();
|
||||
},
|
||||
async upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData(page) {
|
||||
let obj = {
|
||||
page: page.num,
|
||||
per_page: page.size,
|
||||
status: this.order[this.active].key,
|
||||
keyword: this.searchText
|
||||
}
|
||||
this.$api.get(`/v1/order/orders`, {
|
||||
params: obj
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.length)
|
||||
if (page.num == 1) this.dataList = [];
|
||||
this.dataList = this.dataList.concat(res.data);
|
||||
}).catch(err => {
|
||||
this.mescroll.endErr()
|
||||
})
|
||||
},
|
||||
//搜索
|
||||
Search() {
|
||||
this.downCallback()
|
||||
},
|
||||
Change(e) {
|
||||
if (e == '') {
|
||||
this.downCallback()
|
||||
}
|
||||
},
|
||||
//修改状态
|
||||
Parent(e) {
|
||||
let Index = this.dataList.findIndex(item => {
|
||||
return item.id == e
|
||||
})
|
||||
this.dataList.splice(Index, 1)
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.badge {
|
||||
@apply bg-badge;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
<template>
|
||||
<view class="user-sign-bg pb-60rpx">
|
||||
<view class="text-center">
|
||||
<image class="w-562rpx h-64rpx mx-auto mt-50rpx" src="/static/images/user/user_sign-title.png" />
|
||||
</view>
|
||||
<view class="px-base flex justify-between items-end mt-26rpx">
|
||||
<text class="month-u text-white text-3xl font-medium">{{m + 1}}</text>
|
||||
<!-- <image class="w-239rpx h-95rpx" src="/static/images/user/user_sign-rule.png" mode="widthFix" /> -->
|
||||
</view>
|
||||
<view class="px-base">
|
||||
<view class="bg-hex-ffffff bg-opacity-90 rounded-30rpx">
|
||||
<view class="grid grid-cols-7">
|
||||
<view class="text-center text-txBase text-md h-90rpx leading-90rpx" v-for="(item, index) in weekDay"
|
||||
:key="index">{{ item }}</view>
|
||||
</view>
|
||||
|
||||
<view>
|
||||
<view class="grid grid-cols-7 gap-10rpx">
|
||||
<view class="text-txBase h-90rpx text-md flex items-center justify-center" v-for="(item, index) in dates"
|
||||
:key="index" :class="{ 'text-opacity-50': !item.lm }">
|
||||
<view class="h-56rpx w-56rpx flex items-center justify-center" @tap="selectOne(item, $event)"
|
||||
:class="{ sign: isSigned(item.year, item.month + 1, item.date) }">
|
||||
{{ item.date }}
|
||||
</view>
|
||||
<!-- <view class="sign" v-if="isSigned(item.year, item.month + 1, item.date)"></view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="text-center">
|
||||
<!-- 点击签到 -->
|
||||
<image src="/static/images/user/user_sign-btn.png" @tap="onSign" mode="widthFix" class="mx-auto" />
|
||||
</view>
|
||||
|
||||
<view class="px-base">
|
||||
<view class="bg-white rounded-30rpx bg-opacity-90 pb-25rpx">
|
||||
<view class="text-center">
|
||||
<image class="w-full" src="/static/images/user/user_sign-des-title.png" mode="widthFix" />
|
||||
</view>
|
||||
<view class="bg-icon bg-hex-F0FCF7 relative py-9rpx pl-60rpx mx-25rpx mb-38rpx">
|
||||
<view class="text-hex-073020 text-xl font-medium">积分获得方式</view>
|
||||
<view class="text-hex-073020 text-lg">可在商城中通过完成签到,观看健康知识小文章等获取专区积分</view>
|
||||
</view>
|
||||
<view class="bg-icon bg-hex-F0FCF7 relative py-9rpx pl-60rpx mx-25rpx mb-38rpx">
|
||||
<view class="text-hex-073020 text-xl font-medium">积分用途</view>
|
||||
<view class="text-hex-073020 text-lg">可使用积分在商城中去兑换丰富的产品</view>
|
||||
</view>
|
||||
<view class="bg-icon bg-hex-F0FCF7 relative py-9rpx pl-60rpx mx-25rpx mb-38rpx">
|
||||
<view class="text-hex-073020 text-xl font-medium">积分有效期</view>
|
||||
<view class="text-hex-073020 text-lg">以开通店铺当日时间作为有效期开始时间,有效期时间为6个月</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<u-mask :show="show" @tap="show = false">
|
||||
<view class="flex items-center justify-center h-full flex-col">
|
||||
<image class="w-357rpx h-85rpx z-40" src="/static/images/user/user_sign-sucess.png" />
|
||||
<view class="w-511rpx border-4rpx border-primary rounded-15rpx h-303rpx bg-white -mt-42rpx pt-42rpx">
|
||||
<view class="h-full flex flex-col justify-center items-center text-primary">
|
||||
<view class="text-xl font-medium">今日签到获得<text class="text-warning">{{ points }}</text>积分</view>
|
||||
<!-- <view class="text-xl mt-27rpx">累积积分:320</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-mask>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
formatDate,
|
||||
_pad
|
||||
} from '@/utils/filters';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
points: 0,
|
||||
show: false,
|
||||
weekstart: 7,
|
||||
open: true,
|
||||
text: {
|
||||
year: '年',
|
||||
month: '月',
|
||||
week: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
today: '今',
|
||||
},
|
||||
y: new Date().getFullYear(), // 年
|
||||
m: new Date().getMonth(), // 月
|
||||
dates: [], // 当前月日期集合
|
||||
positionTop: 0,
|
||||
monthOpen: true,
|
||||
choose: '',
|
||||
signeddates: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 顶部星期栏目
|
||||
weekDay() {
|
||||
return this.text.week.slice(this.weekstart - 1).concat(this.text.week.slice(0, this.weekstart - 1));
|
||||
},
|
||||
height() {
|
||||
return (this.dates.length / 7) * 80 + 'upx';
|
||||
},
|
||||
},
|
||||
onLoad() {
|
||||
this.dates = this.monthDay(this.y, this.m);
|
||||
!this.open && this.trgWeek();
|
||||
this.getSignDetail();
|
||||
},
|
||||
onShow() {
|
||||
let date = new Date();
|
||||
let y = date.getFullYear();
|
||||
let m = date.getMonth();
|
||||
let d = date.getDate();
|
||||
this.choose = `${y}-${m + 1}-${d}`;
|
||||
},
|
||||
methods: {
|
||||
//获取签到
|
||||
async getSignDetail() {
|
||||
// console.log(this.dates.length);
|
||||
// console.log(this.formatDate(this.dates[0]), this.dates[34])
|
||||
|
||||
const resData = await this.$api.get('/v1/click', {
|
||||
params: {
|
||||
start_at: this.formatDate(this.dates[0]),
|
||||
end_at: this.formatDate(this.dates[this.dates.length - 1]),
|
||||
},
|
||||
});
|
||||
this.signeddates = resData;
|
||||
},
|
||||
//签到
|
||||
async onSign() {
|
||||
const {
|
||||
points
|
||||
} = await this.$api.post('/v1/click');
|
||||
this.getSignDetail();
|
||||
this.points = points;
|
||||
this.show = true;
|
||||
this.$store.dispatch('user/getUserInfo');
|
||||
},
|
||||
|
||||
// 获取当前月份天数
|
||||
monthDay(y, m) {
|
||||
let firstDayOfMonth = new Date(y, m, 1).getDay(); // 当月第一天星期几
|
||||
let lastDateOfMonth = new Date(y, m + 1, 0).getDate(); // 当月最后一天
|
||||
let lastDayOfLastMonth = new Date(y, m , 0).getDate(); // 上一月的最后一天
|
||||
|
||||
let dates = []; // 所有渲染日历
|
||||
let weekstart = this.weekstart == 7 ? 0 : this.weekstart; // 方便进行日期计算,默认星期从0开始
|
||||
let startDay = (() => {
|
||||
// 周初有几天是上个月的
|
||||
if (firstDayOfMonth == weekstart) {
|
||||
return 0;
|
||||
} else if (firstDayOfMonth > weekstart) {
|
||||
return firstDayOfMonth - weekstart;
|
||||
} else {
|
||||
return 7 - weekstart + firstDayOfMonth;
|
||||
}
|
||||
})();
|
||||
let endDay = 7 - ((startDay + lastDateOfMonth) % 7); // 结束还有几天是下个月的
|
||||
for (let i = 1; i <= startDay; i++) {
|
||||
dates.push({
|
||||
date: lastDayOfLastMonth - startDay + i,
|
||||
day: weekstart + i - 1 || 7,
|
||||
month: m - 1 >= 0 ? m - 1 : 11,
|
||||
year: m - 1 >= 0 ? y : y - 1,
|
||||
});
|
||||
}
|
||||
for (let j = 1; j <= lastDateOfMonth; j++) {
|
||||
dates.push({
|
||||
date: j,
|
||||
day: (j % 7) + firstDayOfMonth - 1 || 7,
|
||||
month: m,
|
||||
year: y,
|
||||
lm: true,
|
||||
});
|
||||
|
||||
}
|
||||
for (let k = 1; k <= endDay; k++) {
|
||||
dates.push({
|
||||
date: k,
|
||||
day: (lastDateOfMonth + startDay + weekstart + k - 1) % 7 || 7,
|
||||
month: m + 1 <= 11 ? m + 1 : 0,
|
||||
year: m + 1 <= 11 ? y : y + 1,
|
||||
});
|
||||
}
|
||||
return dates;
|
||||
},
|
||||
formatDate({
|
||||
year,
|
||||
month,
|
||||
date
|
||||
}) {
|
||||
let dy = `${year}-${_pad(month+1)}-${_pad(date)}`;
|
||||
return formatDate(dy, 'yyyy-MM-dd');
|
||||
},
|
||||
// 已经签到处理
|
||||
isSigned(y, m, d) {
|
||||
let flag = false;
|
||||
for (let i = 0; i < this.signeddates.length; i++) {
|
||||
let dy = `${y}-${_pad(m)}-${_pad(d)}`;
|
||||
|
||||
if (this.signeddates[i] == formatDate(dy, 'yyyy-MM-dd')) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
},
|
||||
isToday(y, m, d) {
|
||||
let date = new Date();
|
||||
return y == date.getFullYear() && m == date.getMonth() && d == date.getDate();
|
||||
},
|
||||
// 切换成周模式
|
||||
trgWeek() {
|
||||
this.monthOpen = !this.monthOpen;
|
||||
if (this.monthOpen) {
|
||||
this.positionTop = 0;
|
||||
} else {
|
||||
let index = -1;
|
||||
this.dates.forEach((i, x) => {
|
||||
this.isToday(i.year, i.month, i.date) && (index = x);
|
||||
});
|
||||
this.positionTop = -((Math.ceil((index + 1) / 7) || 1) - 1) * 80;
|
||||
}
|
||||
},
|
||||
// 点击回调
|
||||
selectOne(i, event) {
|
||||
let date = `${i.year}-${i.month + 1}-${i.date}`;
|
||||
let selectD = new Date(date);
|
||||
if (selectD.getMonth() != this.m) {
|
||||
// console.log('不在可选范围内');
|
||||
return false;
|
||||
}
|
||||
this.choose = date;
|
||||
this.$emit('on-click', date);
|
||||
},
|
||||
// 上个月,下个月
|
||||
turning(_action) {
|
||||
if (_action === 'next') {
|
||||
if (this.m + 1 == 12) {
|
||||
this.m = 0;
|
||||
this.y = this.y + 1;
|
||||
} else {
|
||||
this.m = this.m + 1;
|
||||
}
|
||||
} else {
|
||||
if (this.m + 1 == 1) {
|
||||
this.m = 11;
|
||||
this.y = this.y - 1;
|
||||
} else {
|
||||
this.m = this.m - 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.dates = this.monthDay(this.y, this.m);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.user-sign-bg {
|
||||
background: url('/static/images/user/user_sign-bg.png') no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
.month-u {
|
||||
&::after {
|
||||
content: '月';
|
||||
font-size: 70%;
|
||||
}
|
||||
}
|
||||
|
||||
.sign {
|
||||
background: linear-gradient(180deg, rgba(255, 195, 0, 1) 0%, rgba(255, 91, 41, 1) 100%);
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
background: url('/static/images/user/user_sign-sign-icon.png') no-repeat;
|
||||
background-size: 24rpx 24rpx;
|
||||
position: absolute;
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
bottom: -26rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-icon {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -10rpx;
|
||||
top: -10rpx;
|
||||
background: url('/static/images/user/user_sign-des-icon.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
<template>
|
||||
<view>
|
||||
<u-navbar back-icon-color="#000000" title-color="#000000" :border-bottom="false" >
|
||||
<view class="flex-1 text-center text-32rpx">可提转余额</view>
|
||||
<view slot="right" class="pr-base text-28rpx text-primary" @tap="$u.routeAuth('/pageA/withdrawal_details/index')"> 提现明细 </view>
|
||||
</u-navbar>
|
||||
<nav-shadow></nav-shadow>
|
||||
<view class="px-60rpx pt-100rpx">
|
||||
<view class="flex items-center text-txBase text-lg mt-60rpx">
|
||||
<view class="w-120rpx">金额</view>
|
||||
<input placeholder-class="text-txGray" class="inputHeight rounded-lg" v-model="amount" type="number" placeholder="0.00" />
|
||||
</view>
|
||||
<view class="text-right text-txBase text-lg mt-60rpx">可提余额:{{ wallet.balance }}</view>
|
||||
<view class="text-center text-txGray text-md mt-60rpx">提示:请认真填写,避免发生错误!</view>
|
||||
<view class="mt-50rpx">
|
||||
<view class="login-btn" @tap="onSubmit"> 确认提现 </view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 判断是否设置了安全密码 -->
|
||||
<cu-modal
|
||||
v-model="tipsShow"
|
||||
title="您还未设置支付密码?"
|
||||
:showCancelButton="true"
|
||||
confirmText="去设置"
|
||||
@confirm="$u.routeAuth('/pageA/reset_password/secure')"
|
||||
content="提示:请先设置后在进行操作~"
|
||||
></cu-modal>
|
||||
<!-- 账号输入密码确认 -->
|
||||
<password-popup v-model="inputShow" @getPassword="confirm">
|
||||
<view class="text-md text-txBase text-center">转出余额:{{ (amount * 1).toFixed(2) }}</view>
|
||||
<view v-if="passwordError" class="text-txBase text-center text-error">密码错误</view>
|
||||
</password-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mcl } from '@/utils/index.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tipsShow: false,
|
||||
inputShow: false,
|
||||
passwordError: false,
|
||||
content: '你还未设置安全密码,是否立即前往设置?',
|
||||
amount: '',
|
||||
min: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
wallet() {
|
||||
return this.$store.getters.user?.wallet ?? {};
|
||||
},
|
||||
//判断提现的金额是否大于余额
|
||||
judge() {
|
||||
return this.amount.trim() * 1 - this.wallet.balance;
|
||||
},
|
||||
//判断最低金额
|
||||
minamount() {
|
||||
return this.amount.trim() * 1 - this.min * 1;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
//提交
|
||||
onSubmit() {
|
||||
if (this.minamount <= 0) {
|
||||
this.$u.toast(`提现的金额不能为0`);
|
||||
} else if (this.judge > 0) {
|
||||
this.$u.toast(`最高提现金额为${this.wallet.balance}元`);
|
||||
} else {
|
||||
!this.wallet.has_password ? (this.tipsShow = true) : (this.inputShow = true);
|
||||
}
|
||||
},
|
||||
async confirm(e) {
|
||||
this.inputShow = false;
|
||||
try {
|
||||
let obj = {
|
||||
amount: mcl(this.amount, 100),
|
||||
wallet_password: e,
|
||||
};
|
||||
await this.$api.post('/v1/wallet/wallet-to-balance', obj, {
|
||||
custom: {
|
||||
loading: true,
|
||||
toast: false,
|
||||
},
|
||||
});
|
||||
|
||||
this.amount = '';
|
||||
this.$store.dispatch('user/getUserInfo');
|
||||
this.$u.toast('提现成功');
|
||||
uni.redirectTo({
|
||||
url: '/pageA/withdrawal_details/index',
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.errcode == 10001) {
|
||||
this.passwordError = true;
|
||||
this.inputShow = true;
|
||||
} else {
|
||||
this.$u.toast(err.message ?? '系统繁忙,请稍后再试');
|
||||
this.inputShow = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
min-height: 100vh;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.inputHeight {
|
||||
@apply h-80rpx flex-1 border border-txBorder px-base ml-40rpx text-lg text-txBase;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<view>
|
||||
<nav-shadow></nav-shadow>
|
||||
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
|
||||
:up="upOption">
|
||||
<view class="flex items-center text-center py-30rpx text-lg text-txBase">
|
||||
<view class="flex-1">时间</view>
|
||||
<view class="flex-1">金额</view>
|
||||
</view>
|
||||
<view v-for="(item,index) in dataList" :key="index" class="flex items-center text-center pt-base text-lg text-txGray">
|
||||
<view class="flex-1">{{item.created_at}}</view>
|
||||
<view class="flex-1">{{item.change_balance}}</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
export default {
|
||||
mixins: [MescrollMixin], // 使用mixin
|
||||
data() {
|
||||
return {
|
||||
downOption: {
|
||||
auto: false,
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20
|
||||
},
|
||||
noMoreSize: 1
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
};
|
||||
},
|
||||
methods:{
|
||||
downCallback() {
|
||||
this.mescroll.resetUpScroll();
|
||||
this.dataList = []
|
||||
},
|
||||
async upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData(page) {
|
||||
this.$api.get(`/v1/wallet/wallet-logs`, {
|
||||
params: {
|
||||
page: page.num,
|
||||
per_page: page.size,
|
||||
action:'withdraw-balance'
|
||||
}
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.length)
|
||||
if (page.num == 1) this.dataList = [];
|
||||
this.dataList = this.dataList.concat(res.data);
|
||||
}).catch(err => {
|
||||
this.mescroll.endErr()
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
min-height: 100vh;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<template>
|
||||
<view class="flex items-center justify-center flex-col pt-80rpx">
|
||||
<image class="w-400rpx h-400rpx" src="http://zcs-test.oss-cn-chengdu.aliyuncs.com/product-spus/cover/2022-04-22/f6f96cba9ba86197e58ea3c2ce93b2a1.jpg" mode="scaleToFill"></image>
|
||||
<view class="mt-20rpx">请用户通过微信扫码进入小程序生成订单</view>
|
||||
<view class="grid grid-cols-2 gap-x-80rpx text-36rpx mt-120rpx">
|
||||
<view @click="toBack">门店管理</view>
|
||||
<view @click="toSwitchTab">个人中心</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
methods:{
|
||||
toBack(){
|
||||
uni.navigateBack({
|
||||
delta:3
|
||||
})
|
||||
},
|
||||
toSwitchTab(){
|
||||
uni.switchTab({
|
||||
url:'/pages/me/me'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
page{
|
||||
background: #ffffff;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<template>
|
||||
<view class="px-80rpx pt-60rpx text-white text-30rpx font-extrabold">
|
||||
<view class="grid grid-cols-2 gap-x-80rpx gap-y-40rpx">
|
||||
<view @click="$u.route('/pageB/select_store/index')" class="relative bg-hex-f39c12 bg-opacity-60" style="padding-top: 100%">
|
||||
<view class="absolute left-0 w-full top-0 h-full flex items-center justify-center flex-col">
|
||||
<view >帮用户下单</view>
|
||||
</view>
|
||||
</view>
|
||||
<view @click="$u.route('/pageB/user_order/index')" class="relative bg-hex-dd4b39 bg-opacity-60" style="padding-top: 100%">
|
||||
<view class="absolute left-0 w-full top-0 h-full flex items-center justify-center flex-col">
|
||||
<view>用户提货</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
<template>
|
||||
<view>
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<u-navbar>
|
||||
<view class="text-center w-full ">
|
||||
<block v-if="detail.status >= 0">
|
||||
<view class="text-lg font-medium">{{ detail.status | payStatusText }}</view>
|
||||
<view class="text-xs" v-if="detail.status == 0">
|
||||
<u-count-down :timestamp="detail.expires_at" font-size="20" separator="zh" separator-size="20"
|
||||
@end="onCountEnd"></u-count-down>
|
||||
</view>
|
||||
</block>
|
||||
<view v-else class="text-lg font-medium">订单详情</view>
|
||||
</view>
|
||||
</u-navbar>
|
||||
<view class="bg-white mt-20rpx py-base px-40rpx">
|
||||
<block v-for="(item, index) in products" :key="index">
|
||||
<view @tap="$u.routeAuth('/pages/product_details/index', { skuId: index})" class="mb-base">
|
||||
<view class="flex">
|
||||
<u-image border-radius="10" width="190rpx" height="190rpx"
|
||||
src="http://zcs-test.oss-cn-chengdu.aliyuncs.com/product-spus/cover/2022-04-22/f6f96cba9ba86197e58ea3c2ce93b2a1.jpg"
|
||||
:lazy-load="true"></u-image>
|
||||
<view class="flex-1 ml-16rpx w-0 flex justify-between flex-col">
|
||||
<view>
|
||||
<view>
|
||||
<view class="line-2">商品名称商品名称商品名称商品商品名称商品名称商品名称商品商品名称商品名称商品名称商品</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex items-end">
|
||||
<view class="flex-1">
|
||||
<view class="">
|
||||
<view class="flex-1 price-text">390</view>
|
||||
</view>
|
||||
<view class="flex items-center" >
|
||||
<view class="price-text">300</view>
|
||||
<image class="w-58rpx h-58rpx ml-10rpx" src="/static/images/svip.svg" mode=""></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-lg"> x20</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="my-20rpx" v-if="index != products.length - 1">
|
||||
<u-line color="#E5E5E5" />
|
||||
</view>
|
||||
</block>
|
||||
<view class="mt-20rpx">
|
||||
<view class="h-50rpx leading-50rpx flex justify-between">
|
||||
<text class="text-black">商品金额</text>
|
||||
<text class="price-text">390</text>
|
||||
</view>
|
||||
<view class="h-50rpx leading-50rpx flex justify-between">
|
||||
<text class="text-black">运费</text>
|
||||
<text class="price-text">0</text>
|
||||
</view>
|
||||
</view>
|
||||
<u-line color="#E5E5E5" />
|
||||
<view class="flex justify-end mt-base">
|
||||
<text class="text-lg text-black">实付金额:</text>
|
||||
<text class="text-warning text-xl ">¥999</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white mt-20rpx py-base px-40rpx">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="h-50rpx leading-50rpx text-md text-black">订单编号:12345678912345678</view>
|
||||
<view @tap="copy(detail.sn)" class="border border-solid border-hex-A6A6A6 rounded-full px-base py-6rpx">复制</view>
|
||||
</view>
|
||||
<view class="h-50rpx leading-50rpx text-md text-black">下单时间:2022-12-12</view>
|
||||
<view class="h-50rpx leading-50rpx text-md text-black" >支付时间:2022-12-12</view>
|
||||
</view>
|
||||
<!-- 底部导航栏 -->
|
||||
<block>
|
||||
<view class="h-120rpx"></view>
|
||||
<view class="fixed bg-white bottom-0 w-full px-40rpx">
|
||||
<view class="flex justify-end h-100rpx items-center">
|
||||
<view class="border border-hex-A6A6A6 border-solid rounded-full px-20rpx py-10rpx ml-base">取消订单</view>
|
||||
<view class="border border-hex-A6A6A6 border-solid rounded-full px-20rpx py-10rpx ml-base" >立即付款</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniCopy from '@/utils/uni-copy';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isFirstLoading: false,
|
||||
products: [{}, {}, {}],
|
||||
detail: {}
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
//复制
|
||||
copy(val) {
|
||||
uniCopy({
|
||||
content: val ?? '',
|
||||
success: (res) => {
|
||||
this.$u.toast(res);
|
||||
},
|
||||
error: (e) => {
|
||||
this.$u.toast(e);
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<view class="py-base flex items-start">
|
||||
<u-image class="flex-none" width="190" height="190" border-radius="10" :src="goods.cover" :lazy-load="true">
|
||||
</u-image>
|
||||
<view class="flex-1 ml-15rpx">
|
||||
<view class="line-1 w-full text-txBase">{{goods.name}}</view>
|
||||
<view class="price-text flex-1 text-txBase text-md mt-30rpx">{{goods.sell_price}}元</view>
|
||||
<view class="flex items-center justify-between mt-30rpx flex-1">
|
||||
<view class="text-txBase text-md price-text">{{goods.vip_price}}元 vip</view>
|
||||
<view v-if="isAdd" class="bg-hex-ef4444 text-center px-20rpx py-8rpx text-white rounded-10rpx" @click="add">添加</view>
|
||||
<view v-else>
|
||||
<u-number-box :min="1" :value="goods.num">
|
||||
</u-number-box>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props:{
|
||||
isAdd:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
},
|
||||
goods:{
|
||||
type:Object,
|
||||
default:()=>{}
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
goodsItem:{},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods:{
|
||||
//添加商品
|
||||
add(){
|
||||
this.$emit('addGood',this.goods.id)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
<template>
|
||||
<view class="">
|
||||
<view class="flex items-center px-40rpx text-32rpx bg-white py-30rpx">
|
||||
<view class="mr-15rpx">商品信息</view>
|
||||
<u-search @search="searchGoods" class="flex-1" placeholder="货号或名称" v-model="keyword" :show-action="false">
|
||||
</u-search>
|
||||
</view>
|
||||
<!-- 有商品时 -->
|
||||
<block v-if="!isShow">
|
||||
<!-- 搜索的商品列表 -->
|
||||
<view class="px-30rpx bg-white" v-if="goodItem.id">
|
||||
<GoodsItem :isAdd="true" :goods="goodItem" @addGood="addGood" />
|
||||
</view>
|
||||
<!-- 提货数量 -->
|
||||
<view class="px-30rpx bg-white mt-20rpx ">
|
||||
<view class="py-20rpx flex w-full items-start" v-for="(item,index) in chooseList" :key="index">
|
||||
<view
|
||||
class="w-38rpx mt-8rpx h-38rpx rounded-full border border-hex-ef4444 border-solid text-center leading-38rpx text-hex-ef4444">
|
||||
{{index+1}}
|
||||
</view>
|
||||
<view class="flex-1 ml-15rpx">
|
||||
<view class="flex items-center">
|
||||
<view class="flex flex-1 items-center justify-between">
|
||||
<view>默认已提货量》</view>
|
||||
<view>
|
||||
<u-number-box :min="1" :max="3" :value="1">
|
||||
</u-number-box>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<GoodsItem class="px-0" />
|
||||
<u-line v-if="list.length-1!=index" :hair-line="false" color="#c8c9cc"></u-line>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="h-180rpx"></view>
|
||||
<!-- 底部按钮 -->
|
||||
<view class="flex items-center justify-end bg-white bottom-card z-99 fixed inset-x-0">
|
||||
<view class="flex items-center">
|
||||
<view>
|
||||
合计
|
||||
<text class="ml-10rpx price-text text-hex-ef4444">15元</text>
|
||||
</view>
|
||||
<view class="ml-10rpx">
|
||||
vip
|
||||
<text class="ml-10rpx price-text text-hex-ef4444">15元</text>
|
||||
</view>
|
||||
</view>
|
||||
<view @click="$u.route('/pageB/code/index')"
|
||||
class="bg-hex-ef4444 h-100rpx w-210rpx text-white text-center leading-100rpx ml-30rpx">生成二维码</view>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 无商品时 -->
|
||||
<block v-else>
|
||||
<view class="flex items-center justify-center flex-col mt-80rpx">
|
||||
<image src="http://cdn.uviewui.com/uview/empty/car.png" mode="aspectFill"></image>
|
||||
<view class="text-hex-909399 -mt-20rpx">请搜索您需要的商品</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GoodsItem from './components/goods-item.vue'
|
||||
export default {
|
||||
components: {
|
||||
GoodsItem
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShow:true,
|
||||
id: '',
|
||||
keyword: '',
|
||||
list: [], //搜索的商品
|
||||
chooseList: [], //选择的商品
|
||||
goodItem:{},
|
||||
};
|
||||
},
|
||||
onLoad({
|
||||
id
|
||||
}) {
|
||||
this.id = id
|
||||
},
|
||||
methods: {
|
||||
async searchGoods() {
|
||||
this.isShow=false
|
||||
const resDate = await this.$api.get(`/v1/product/products`, {
|
||||
params: {
|
||||
keyword: this.keyword
|
||||
}
|
||||
})
|
||||
this.list = resDate.data
|
||||
if (this.list.length == 0) {
|
||||
return this.$u.toast('暂未搜索到该商品')
|
||||
}
|
||||
let goodsObj = resDate.data.length > 0 ? resDate.data[0] : {}
|
||||
goodsObj.defaultNum = 1
|
||||
goodsObj.num = 1
|
||||
this.goodItem = goodsObj
|
||||
console.log( this.goodItem)
|
||||
},
|
||||
//添加商品
|
||||
addGood(e) {
|
||||
const result=this.chooseList.findIndex(item=> item.id=e)
|
||||
console.log(result);
|
||||
if(result==-1){
|
||||
this.chooseList.unshift(this.goodItem)
|
||||
}else{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.bottom-card {
|
||||
box-shadow: 0px -4rpx 8rpx rgba(0, 0, 0, 0.25);
|
||||
bottom: var(--window-bottom);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<view class=" text-30rpx font-extrabold">
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback"
|
||||
:down="downOption" :up="upOption">
|
||||
<view class="grid grid-cols-2 gap-x-80rpx gap-y-40rpx pt-60rpx px-80rpx">
|
||||
<view @click="$u.route('/pageB/select_product/index',{id:item.id})" class="flex items-center justify-center flex-col"
|
||||
v-for="(item,index) in dataList" :key="index">
|
||||
<!-- <view class="w-240rpx h-240rpx bg-hex-367fa9 bg-opacity-60"></view> -->
|
||||
<u-image class="flex-none" width="240" height="240" :src="item.image" :lazy-load="true">
|
||||
</u-image>
|
||||
<view class="mt-15rpx">{{item.title}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
export default {
|
||||
mixins: [MescrollMixin], // 使用mixin
|
||||
data() {
|
||||
return {
|
||||
isFirstLoading:true,
|
||||
downOption: {},
|
||||
upOption: {
|
||||
page: {
|
||||
size: 20
|
||||
},
|
||||
noMoreSize: 1
|
||||
},
|
||||
dataList: [], //获取到的数据
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
setTimeout(()=>{
|
||||
this.isFirstLoading=false
|
||||
},300)
|
||||
},
|
||||
methods: {
|
||||
downCallback() {
|
||||
this.mescroll.resetUpScroll();
|
||||
this.dataList = []
|
||||
},
|
||||
async upCallback(page) {
|
||||
this.loadData(page);
|
||||
},
|
||||
loadData(page) {
|
||||
this.$api.get(`/v1/store`, {
|
||||
params: {
|
||||
page: page.num,
|
||||
per_page: page.size
|
||||
}
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.length)
|
||||
if (page.num == 1) this.dataList = [];
|
||||
this.dataList = this.dataList.concat(res.data);
|
||||
}).catch(err => {
|
||||
this.mescroll.endErr()
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<template>
|
||||
<view class="bg-white px-40rpx" @tap="$u.route('/pageB/user_select_pro/index',{id:1})">
|
||||
<view class="h-78rpx flex justify-between items-center">
|
||||
<text class="text-lg">订单编号:12345678954565</text>
|
||||
<text class="text-md text-hex-2A82E4">发货中</text>
|
||||
</view>
|
||||
<u-line color="#E5E5E5" />
|
||||
<block v-for="(ite,ind) in item.products" :key="ind">
|
||||
<view class="flex py-base">
|
||||
<u-image border-radius="10" width="160rpx" height="160rpx" src="http://zcs-test.oss-cn-chengdu.aliyuncs.com/product-spus/cover/2022-04-22/f6f96cba9ba86197e58ea3c2ce93b2a1.jpg" :lazy-load="true"></u-image>
|
||||
<view class="flex-1 ml-16rpx w-0">
|
||||
<view class="h-70rpx">
|
||||
<view class="line-2">商品名称商品名称商品名称商品商品名称商品名称商品名称商品商品名称商品名称商品名称商品</view>
|
||||
</view>
|
||||
<view class="text-right text-lg mt-20rpx"> x 20 </view>
|
||||
</view>
|
||||
</view>
|
||||
<u-line color="#E5E5E5" />
|
||||
</block>
|
||||
<view class="flex items-center justify-between h-85rpx">
|
||||
<text class="text-md">2022-2-15</text>
|
||||
<text class="text-lg font-medium">共件5 应付总额:¥8888</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props:{
|
||||
item:{
|
||||
type:Object,
|
||||
default:()=>{}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<view>
|
||||
<!-- 搜索框 -->
|
||||
<view class="flex items-center px-40rpx text-32rpx bg-white py-30rpx">
|
||||
<view class="mr-15rpx">手机号:</view>
|
||||
<u-search class="flex-1" placeholder="用户手机号" v-model="searchText" :show-action="false"></u-search>
|
||||
</view>
|
||||
<view class="pt-base">
|
||||
<block v-for="(item,index) in dataList" :key="index">
|
||||
<view class="mb-24rpx">
|
||||
<order-item @Parent=Parent :item="item" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import OrderItem from './conponents/order-item.vue';
|
||||
export default {
|
||||
components: {
|
||||
OrderItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchText:'',
|
||||
dataList:[{products:[{}]},{products:[{},{}]},{products:[{},{},{}]}]
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.badge {
|
||||
@apply bg-badge;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<view class="pt-20rpx">
|
||||
<block v-for="(item,index) in list" :key="index">
|
||||
<view class="bg-white px-40rpx notFrist">
|
||||
<view class="flex py-base">
|
||||
<u-image border-radius="10" width="170rpx" height="170rpx"
|
||||
src="http://zcs-test.oss-cn-chengdu.aliyuncs.com/product-spus/cover/2022-04-22/f6f96cba9ba86197e58ea3c2ce93b2a1.jpg"
|
||||
:lazy-load="true"></u-image>
|
||||
<view class="flex-1 ml-16rpx w-0">
|
||||
<view class="h-70rpx">
|
||||
<view class="line-2">商品名称商品名称商品名称商品商品名称商品名称商品名称商品商品名称商品名称商品名称商品</view>
|
||||
</view>
|
||||
<view class="flex items-center justify-between mt-20rpx">
|
||||
<text>待提货</text>
|
||||
<text>x 20</text>
|
||||
</view>
|
||||
<view class="flex items-center justify-between ">
|
||||
<text>已提货</text>
|
||||
<text>x 20</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<u-line color="#E5E5E5" />
|
||||
<view class="flex items-center justify-between h-85rpx">
|
||||
<view>本次提货</view>
|
||||
<view>
|
||||
<u-number-box :min="1" :max="3" :value="1">
|
||||
</u-number-box>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<view class="h-170rpx"></view>
|
||||
<!-- 底部按钮 -->
|
||||
<view class="fixed bottom-0 left-0 w-full bg-white shadow-t ">
|
||||
<view class="h-100rpx flex items-center px-24rpx justify-between text-hex-999999">
|
||||
<view class="h-72rpx flex-1 leading-72rpx text-center rounded-full bg-hex-ED1B13 text-26rpx text-center text-white">提货</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
list: [{}, {}]
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shadow-t {
|
||||
box-shadow: 0px -2px 5px rgb(0 0 0 / 11%);
|
||||
}
|
||||
.notFrist:not(:first-child){
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,626 @@
|
|||
{
|
||||
"pages": [
|
||||
//pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
|
||||
//#ifdef APP-PLUS
|
||||
//启动页
|
||||
{
|
||||
"path": "pages/guide/judge",
|
||||
"style": {
|
||||
"enablePullDownRefresh": false,
|
||||
"onReachBottomDistance": 100,
|
||||
"navigationStyle": "custom",
|
||||
"app-plus": {
|
||||
"contentAdjust": false,
|
||||
"bounce": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/guide/guide",
|
||||
"style": {
|
||||
"enablePullDownRefresh": false,
|
||||
"onReachBottomDistance": 100,
|
||||
"navigationStyle": "custom",
|
||||
"app-plus": {
|
||||
"contentAdjust": false,
|
||||
"bounce": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "首页"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/upgrade_popup/index",
|
||||
"style": {
|
||||
"disableScroll": true,
|
||||
"navigationStyle": "custom",
|
||||
"app-plus": {
|
||||
"animationType": "fade-in",
|
||||
"background": "transparent",
|
||||
"backgroundColor": "rgba(0,0,0,0)",
|
||||
"popGesture": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/sort/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "分类"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/search/search",
|
||||
"style": {
|
||||
"navigationBarTitleText": "搜索",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"animationType": "slide-in-bottom",
|
||||
"animationDuration": 300
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/comment/comment",
|
||||
"style": {
|
||||
"navigationBarTitleText": "评论",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/confirm_order/confirm_order",
|
||||
"style": {
|
||||
"navigationBarTitleText": "确认订单",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/search/search_result",
|
||||
"style": {
|
||||
"navigationBarTitleText": "搜索结果",
|
||||
"enablePullDownRefresh": false,
|
||||
"app-plus": {
|
||||
"animationType": "slide-in-bottom"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/product_details/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品详情",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "pages/shop_cart/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "购物车"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/me/me",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人中心",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/index",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "登录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/register/index",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "注册"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/confirm_payment/confirm_payment",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "确认支付"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/payment_results/payment_results",
|
||||
"style": {
|
||||
"navigationBarTitleText": "支付结果",
|
||||
"navigationStyle": "default",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/healthy/healthy",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/article_details/article_details",
|
||||
"style": {
|
||||
"navigationBarTitleText": "文章详情",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order_details/index",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "订单详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/apply_refund/index",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "申请售后"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/goods_logistics/index",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "物流信息"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/publish_evaluation/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发表评价",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/my_package/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的包裹",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/logistics_information/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "物流信息",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/about_us/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "关于我们",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/switch_account/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "切换账号",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/auxiliary_cart/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "购物车(非tabbar)",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/web_view/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/feedback/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "意见反馈",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/development/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "开发中",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/welcome/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/bargain/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "促销活动",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/bargain/invite",
|
||||
"style": {
|
||||
"navigationBarTitleText": "邀请砍价",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/healthy/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康首页",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/points/record",
|
||||
"style": {
|
||||
"navigationBarTitleText": "积分明细",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/points/swap",
|
||||
"style": {
|
||||
"navigationBarTitleText": "积分兑换",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/points/swap-record",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的兑换",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
}
|
||||
],
|
||||
"subPackages": [{
|
||||
"root": "pageA",
|
||||
"pages": [{
|
||||
"path": "personal/personal",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人信息",
|
||||
"navigationStyle": "default",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "news/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "消息",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "membership_interests/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "会员权益",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "special_area/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "直通车专区",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "user_order/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "after_sale/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "售后",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "after_sales_detail/index",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "售后详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "after_sales_logistics/index",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "售后详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "user_sign/index",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "签到"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "collection/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "收藏",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "address/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "地址管理",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "new_address/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "新增地址",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "reset_password/index",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "密码设置"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "reset_password/login",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "密码修改"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "reset_password/secure",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "安全密码"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "reset_password/reset_security",
|
||||
"style": {
|
||||
"navigationBarTitleText": "重置安全密码",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "coupon/coupon",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的优惠卷",
|
||||
"navigationStyle": "default",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "history/index",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "浏览记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "fan_list/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "粉丝列表",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invite_friends/index",
|
||||
"style": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "邀请好友"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "my_account/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的账户",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "balance_transfer/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "余额转账",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "balance_transfer_details/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "余额转账明细",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "withdrawal_balance/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "可提转余额",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "withdrawal_details/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "提现明细",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "bank_card/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "提现到银行卡",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "bank_details/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "提现记录",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "my_bank/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的银行卡",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pageB",
|
||||
"pages": [
|
||||
{
|
||||
"path": "index/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "门店管理",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "select_store/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择门店",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "select_product/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择商品",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "code/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "二维码",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "user_order/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "用户提货",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "user_select_pro/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择商品",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "order_details/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单详情",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "uni-app",
|
||||
"navigationBarBackgroundColor": "#FFFFFF",
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"navigationStyle": "custom",
|
||||
"app-plus": {
|
||||
"bounce": "none",
|
||||
"scrollIndicator": "none"
|
||||
}
|
||||
},
|
||||
"tabBar": {
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"color": "#A8A8A8",
|
||||
"selectedColor": "#378264",
|
||||
"borderStyle": "white",
|
||||
"list": [{
|
||||
"iconPath": "static/tabbar/index.png",
|
||||
"selectedIconPath": "static/tabbar/index_sel.png",
|
||||
"pagePath": "pages/index/index",
|
||||
"text": "主页"
|
||||
},
|
||||
{
|
||||
"iconPath": "static/tabbar/class.png",
|
||||
"selectedIconPath": "static/tabbar/class_sel.png",
|
||||
"pagePath": "pages/sort/index",
|
||||
"text": "分类"
|
||||
},
|
||||
{
|
||||
"iconPath": "static/tabbar/cart.png",
|
||||
"selectedIconPath": "static/tabbar/cart_sel.png",
|
||||
"pagePath": "pages/shop_cart/index",
|
||||
"text": "购物车"
|
||||
},
|
||||
{
|
||||
"iconPath": "static/tabbar/healthy.png",
|
||||
"selectedIconPath": "static/tabbar/healthy_sel.png",
|
||||
"pagePath": "pages/healthy/index",
|
||||
"text": "健康"
|
||||
},
|
||||
{
|
||||
"iconPath": "static/tabbar/my.png",
|
||||
"selectedIconPath": "static/tabbar/my_sel.png",
|
||||
"pagePath": "pages/me/me",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
},
|
||||
"easycom": {
|
||||
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<view>
|
||||
<u-parse :html="content"></u-parse>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
content: ''
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
// this.getDate()
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,358 @@
|
|||
<template>
|
||||
<view class="pb-50rpx">
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<view class="bg-primary h-420rpx p-base bottom-bg">
|
||||
<view class="text-xl font-medium text-white">提交申请</view>
|
||||
<view class="py-30rpx -mx-base mt-40rpx">
|
||||
<cu-steps mode="icon" icon="checkmark-circle-fill" active-color="#ffffff" active-text-color="#fff"
|
||||
un-active-text-color="#fff" un-active-color="#A6A6A6" :list="numList" :current="-1" :text-style="{
|
||||
marginTop: '30rpx',
|
||||
color: '#fff',
|
||||
}"></cu-steps>
|
||||
</view>
|
||||
</view>
|
||||
<view class="-mt-120rpx px-base relative">
|
||||
<view class="bg-white rounded-xs card h-170rpx px-24rpx py-18rpx">
|
||||
<view class="text-lg text-black">提交申请</view>
|
||||
<view class="text-lg text-hex-808080 mt-25rpx">请描述您的问题,并上传相关资料。</view>
|
||||
</view>
|
||||
|
||||
<view class="bg-white rounded-xs mt-30rpx p-base">
|
||||
<view class="text-lg text-black">商品信息</view>
|
||||
<view class="mt-16rpx flex">
|
||||
<u-image class="flex-none" border-radius="15rpx" width="190" height="190" :src="goods.cover"
|
||||
:lazy-load="true"></u-image>
|
||||
<view class="ml-10rpx">
|
||||
<view class="text-hex-808080 text-lg h-90rpx">{{ goods.name }}</view>
|
||||
<view class="text-black text-lg mt-6rpx"> 单价:¥{{ goods.sell_price }} </view>
|
||||
<view class="text-right">
|
||||
<u-number-box v-model="num" :min="1" :max="goods.quantity"></u-number-box>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white rounded-xs mt-30rpx p-base">
|
||||
<view class="bg-white rounded-xs ">
|
||||
<view class="flex text-lg items-center" @tap="typeShow = true">
|
||||
<view class="w-120rpx">
|
||||
<text class="imt">类型:</text>
|
||||
</view>
|
||||
<view class="flex-1 text-right text-hex-808080">
|
||||
<text class="mr-10rpx">{{ typeText }}</text>
|
||||
<u-icon name="arrow-right"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-lg text-black mt-10rpx imt">问题描述 </view>
|
||||
<view class="mt-10rpx">
|
||||
<textarea class="min-h-130rpx w-full" placeholder-style="text-h999" placeholder="为了更好的解决您的问题,请尽量描述详细"
|
||||
maxlength="100" auto-height v-model="content"></textarea>
|
||||
<!-- 图片 -->
|
||||
<view class="py-12rpx grid grid-cols-3 gap-20rpx">
|
||||
<view class="relative w-full" style="padding-top: 100%;" v-for="(img, index) in tempFilePaths" :key="index">
|
||||
<view class="img absolute left-0 top-0 w-full h-full">
|
||||
<image class="img1 w-full h-full" :src="img"></image>
|
||||
<view
|
||||
class="absolute bg-hex-f30606 rounded-full w-48rpx h-48rpx flex items-center justify-center -top-10rpx -right-10rpx text-gray"
|
||||
@tap="onDeleteImg(index)">
|
||||
<u-icon size="20" color="#ffffff" name="close"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="relative w-full" style="padding-top: 100%;" v-if="tempFilePaths.length < maxImgLength">
|
||||
<view class="img absolute left-0 top-0 w-full h-full" @tap="openImage">
|
||||
<u-icon name="camera"></u-icon>
|
||||
<text class="text-28rpx mt-6rpx">图片</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 字数统计 -->
|
||||
<!-- <view class="text-h999 absolute right-18rpx bottom-30rpx">{{ content.length }}/100</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="login-btn my-35rpx px-base" @tap="onSubmit"> 提交申请 </view>
|
||||
</view>
|
||||
|
||||
<u-select v-model="typeShow" @confirm="onChangeType" :list="typeList" :default-value="type"></u-select>
|
||||
<!-- <u-action-sheet :list="list" @tap="uploadImge" v-model="show"></u-action-sheet> -->
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
album,
|
||||
camera
|
||||
} from '@/utils/author.js'
|
||||
import uploadImage from '@/utils/ossutil/uploadFile.js';
|
||||
import loadingView from '../../components/loading-view/loading-view.vue';
|
||||
export default {
|
||||
components: { loadingView },
|
||||
data() {
|
||||
return {
|
||||
isFirstLoading:true,
|
||||
show: false,
|
||||
list: [{
|
||||
text: '拍摄',
|
||||
fontSize: 28,
|
||||
color: '#383838'
|
||||
},
|
||||
{
|
||||
text: '从相册选择',
|
||||
fontSize: 28,
|
||||
color: '#383838'
|
||||
}
|
||||
],
|
||||
id: '',
|
||||
goodsId: '',
|
||||
tempFilePaths: [], //本地保存的图片
|
||||
imgList: [],
|
||||
num: 1,
|
||||
maxImgLength: 6,
|
||||
type: [],
|
||||
content: '',
|
||||
typeShow: false,
|
||||
orderInfo: {},
|
||||
|
||||
numList: [{
|
||||
name: '提交申请',
|
||||
},
|
||||
{
|
||||
name: '客服审核',
|
||||
},
|
||||
{
|
||||
name: '客户确认',
|
||||
},
|
||||
{
|
||||
name: '完成',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
typeText() {
|
||||
return this.typeList[this.type[0]]?.label ?? '请选择';
|
||||
},
|
||||
goods() {
|
||||
return this.orderInfo?.products?.find(({
|
||||
id
|
||||
}) => id == this.goodsId) ?? {};
|
||||
},
|
||||
typeList() {
|
||||
const price = this.goods.total_amount
|
||||
const isprice = price > 0 ? true : false
|
||||
let arr = [{
|
||||
value: 1,
|
||||
label: '退款退货',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '退款',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '换货',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '漏发',
|
||||
},
|
||||
]
|
||||
if (this.goods.is_gift) {
|
||||
arr = [{
|
||||
value: 3,
|
||||
label: '换货',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '漏发',
|
||||
},
|
||||
]
|
||||
} else if (!this.goods.is_gift && !isprice) {
|
||||
arr = [{
|
||||
value: 1,
|
||||
label: '退款退货',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '换货',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '漏发',
|
||||
},
|
||||
]
|
||||
}
|
||||
return arr
|
||||
}
|
||||
},
|
||||
onLoad({
|
||||
id,
|
||||
goodsId
|
||||
}) {
|
||||
this.id = id;
|
||||
this.goodsId = goodsId;
|
||||
this.getOrderInfo();
|
||||
},
|
||||
methods: {
|
||||
async getOrderInfo() {
|
||||
try {
|
||||
const resData = await this.$api.get(`/v1/order/orders/${this.id}`);
|
||||
this.orderInfo = resData;
|
||||
} catch (error) {
|
||||
|
||||
}finally{
|
||||
this.isFirstLoading = false
|
||||
}
|
||||
},
|
||||
//商品提交
|
||||
async onSubmit() {
|
||||
if (this.typeList[this.type[0]]?.value == null || this.typeList[this.type[0]]?.value == '') return this.$u
|
||||
.toast('请选择类型');
|
||||
if (this.content == null || this.content == '') return this.$u.toast('请填写描述');
|
||||
if (!this.tempFilePaths.length) return this.$u.toast('请上传相关资料');
|
||||
await this.uploads()
|
||||
const params = {
|
||||
type: this.typeList[this.type[0]]?.value,
|
||||
order_product_id: this.goodsId,
|
||||
description: this.content,
|
||||
images: this.imgList,
|
||||
num: this.num,
|
||||
};
|
||||
// if (params.type == null || params.type == '') return this.$u.toast('请选择类型');
|
||||
// if (params.description == null || params.description == '') return this.$u.toast('请填写描述');
|
||||
// if (!params.images.length) return this.$u.toast('请上传相关资料');
|
||||
const {
|
||||
id
|
||||
} = await this.$api.post('/v1/after-sales', params);
|
||||
this.$u.route({
|
||||
url: '/pageA/after_sales_detail/index',
|
||||
type: 'redirectTo',
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
},
|
||||
//类型改变
|
||||
onChangeType(val) {
|
||||
this.type = [this.typeList.findIndex((e) => e.value == val[0].value)];
|
||||
},
|
||||
onDeleteImg(index) {
|
||||
this.tempFilePaths.splice(index, 1);
|
||||
},
|
||||
//上传的图片
|
||||
async uploadImge(index) {
|
||||
//相机
|
||||
if (index == 0) {
|
||||
// #ifdef APP-PLUS
|
||||
// let result = await camera()
|
||||
// if (!result) return
|
||||
// #endif
|
||||
this.openImage('camera')
|
||||
} else {
|
||||
//相册
|
||||
// #ifdef APP-PLUS
|
||||
// let result = await album()
|
||||
// if (!result) return
|
||||
// #endif
|
||||
this.openImage('album')
|
||||
}
|
||||
},
|
||||
openImage(val) {
|
||||
uni.chooseImage({
|
||||
count: this.maxImgLength - this.tempFilePaths.length,
|
||||
// sourceType: [val],
|
||||
sizeType: ['compressed'],
|
||||
success: async (res) => {
|
||||
res.tempFilePaths.forEach(item => {
|
||||
this.tempFilePaths.push(item)
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
},
|
||||
//上传
|
||||
async uploads() {
|
||||
const resData = await this.$api.post('/v1/oss-sts');
|
||||
// const host = `https://${resData.bucket}.${resData.region_id}.aliyuncs.com/`
|
||||
const host = `https://${resData.domain}/`;
|
||||
const arr = []
|
||||
try {
|
||||
for (let i = 0; i < this.tempFilePaths.length; i++) {
|
||||
const upFile = new Promise((r) => {
|
||||
uploadImage(
|
||||
this.tempFilePaths[i], {
|
||||
uploadImageUrl: host,
|
||||
AccessKeySecret: resData.AccessKeySecret,
|
||||
OSSAccessKeyId: resData.AccessKeyId,
|
||||
stsToken: resData.SecurityToken,
|
||||
timeout: 3600,
|
||||
dir: 'app/order/',
|
||||
},
|
||||
(result) => {
|
||||
this.imgList.push(result)
|
||||
r()
|
||||
},
|
||||
(err) => {
|
||||
console.log(err);
|
||||
},
|
||||
);
|
||||
})
|
||||
arr.push(upFile)
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
this.$u.toast(err.message || err.errMsg);
|
||||
}
|
||||
return Promise.all(arr)
|
||||
},
|
||||
onUpload() {
|
||||
const canCount = this.maxImgLength - this.tempFilePaths.length;
|
||||
if (canCount <= 0) {
|
||||
return this.$u.toast('最多上传6张!');
|
||||
}
|
||||
this.imgList = []
|
||||
this.show = true
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.bottom-bg {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
background: $u-type-primary;
|
||||
clip-path: ellipse(120% 100% at 50% 0%);
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.imt {
|
||||
position: relative;
|
||||
padding-left: 20rpx;
|
||||
|
||||
&::before {
|
||||
content: '*';
|
||||
color: #d43030;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: #999;
|
||||
border-radius: 8rpx;
|
||||
background-color: #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
<template>
|
||||
<view>
|
||||
<!-- 导航栏 -->
|
||||
<!-- 图片 -->
|
||||
<block v-if="info.media_type==0">
|
||||
<u-image height="400rpx" :src="info.cover" :lazy-load="true"></u-image>
|
||||
</block>
|
||||
<!-- 视频 -->
|
||||
<block v-if="info.media_type==3">
|
||||
<video class="w-full h-400rpx" object-fit="contain" :poster="info.cover" id="myVideo"
|
||||
:src="info.media_content[0]"></video>
|
||||
</block>
|
||||
<!-- 轮播图 -->
|
||||
<block v-if="info.media_type==1">
|
||||
<cu-swiper indicator-pos="bottomRight" mode="number" height="400" border-radius="0" :list="swiperList">
|
||||
</cu-swiper>
|
||||
</block>
|
||||
<!-- 音频 -->
|
||||
<block v-if="info.media_type==2">
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<view class=" pt-base flex items-center justify-center">
|
||||
<audio :src="info.media_content[0]" :poster="info.cover" controls id="myAudio"></audio>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view @tap="audioClick" class=" pt-base flex items-center justify-center">
|
||||
<audio :src="info.media_content[0]" :poster="info.cover" controls id="myAudio"></audio>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</block>
|
||||
<!-- -->
|
||||
<view class="px-36rpx mt-40rpx text-xl font-extrabold text-txBase">{{info.title}}</view>
|
||||
<view class="px-36rpx mt-15rpx text-md text-txBase">{{info.subtitle}}</view>
|
||||
<view class="px-36rpx flex items-center justify-between text-md text-txGray">
|
||||
<view class="">{{info.created_at}}</view>
|
||||
<view @tap="getThumbs" class="flex items-center">
|
||||
<u-icon :color="info.like_status ? '#378264' : ''" size="48"
|
||||
:name="info.like_status ? 'thumb-up-fill' : 'thumb-up'"></u-icon>
|
||||
<view class="ml-base">{{info.likes}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mx-base mt-base h-2rpx bg-txBorder"></view>
|
||||
<view class="px-36rpx mt-30rpx text-md text-txBase leading-60rpx">
|
||||
<u-parse :tag-style="style" :html="info.content"></u-parse>
|
||||
</view>
|
||||
<!-- 倒计时 -->
|
||||
<view v-if="isShow"
|
||||
class="w-100rpx text-center borders text-lg text-txBase fixed right-0 z-8 top-500rpx bg-primary bg-opacity-60 ">
|
||||
{{Time}}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
swiperList: [], //轮播图
|
||||
info: {},
|
||||
Time: 15,
|
||||
val: '',
|
||||
isShow: false,
|
||||
style: {
|
||||
// img:'width:100% !important',
|
||||
table: 'width:100% !important',
|
||||
p: 'text-indent:20px'
|
||||
},
|
||||
isPlaying:false,
|
||||
audioCtx:''
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
async onLoad({
|
||||
id
|
||||
}) {
|
||||
await this.getDate(id)
|
||||
await this.getDown()
|
||||
},
|
||||
onReady() {
|
||||
this.audioCtx = uni.createAudioContext('myAudio')
|
||||
},
|
||||
onUnload() {
|
||||
clearInterval(this.val)
|
||||
this.Time = 15,
|
||||
this.isShow = false
|
||||
},
|
||||
methods: {
|
||||
audioClick(){
|
||||
this.isPlaying=!this.isPlaying
|
||||
if(this.isPlaying){
|
||||
this.audioCtx.play();
|
||||
}else{
|
||||
this.audioCtx.pause();
|
||||
}
|
||||
},
|
||||
async getDate(id) {
|
||||
let resDate = await this.$api.get('/v1/articles/' + id)
|
||||
//处理轮播图
|
||||
if (resDate.media_type == 1) {
|
||||
this.getSwiper(resDate.media_content)
|
||||
}
|
||||
if (resDate.points > 0 && !resDate.has_read) {
|
||||
this.isShow = true
|
||||
}
|
||||
this.info = resDate
|
||||
},
|
||||
getSwiper(val) {
|
||||
let arr = val.split(',')
|
||||
this.swiperList = arr
|
||||
},
|
||||
//点赞
|
||||
async getThumbs() {
|
||||
await this.$api.post(`/v1/articles/${this.info.id}/like`, {}, {
|
||||
custom: {
|
||||
loading: true
|
||||
}
|
||||
});
|
||||
this.info.like_status = true;
|
||||
this.info.likes += 1
|
||||
this.$u.toast('点赞成功');
|
||||
uni.$emit('isUp', {
|
||||
id: this.info.id
|
||||
})
|
||||
},
|
||||
//倒计时
|
||||
getDown() {
|
||||
if (this.isShow && !this.info.has_read) {
|
||||
this.val = setInterval(() => {
|
||||
this.Time--
|
||||
if (this.Time == 0) {
|
||||
this.Time = 15
|
||||
clearInterval(this.val)
|
||||
this.isShow = false
|
||||
this.$api.post(`/v1/articles/${this.info.id}/read`).then(({
|
||||
points
|
||||
}) => {
|
||||
this.$store.dispatch('user/getUserInfo')
|
||||
this.$u.toast(`阅读成功,+${points}积分`);
|
||||
})
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: #FFFFFF;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.borders {
|
||||
border-top-left-radius: 20rpx;
|
||||
border-bottom-left-radius: 20rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<view>
|
||||
<Cart :isBack="true"></Cart>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Cart from '../shop_cart/index'
|
||||
export default {
|
||||
components: {
|
||||
Cart
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<view v-if="goods.is_online" class="rounded-xs box-show" @tap.stop="$u.route('/pages/product_details/index', { skuId: goods.id, type: 1 })">
|
||||
<cu-image :borderTopLeftRadius="24" :borderTopRightRadius="24" width="100%" height="345rpx" :src="goods.cover" :lazy-load="true"></cu-image>
|
||||
<view class="px-base">
|
||||
<view class="text-txBase line-1 w-300rpx mt-10rpx">{{ goods.name }}</view>
|
||||
<view class="flex items-center justify-between py-base">
|
||||
<view class="text-lg -mt-5rpx text-txSvip">¥{{ goods.sell_price }}</view>
|
||||
<view class="bg-hex-e81300 rounded-8rpx px-20rpx h-44rpx leading-44rpx text-white text-26rpx">购买</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
//数据
|
||||
goods: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
data() {
|
||||
return this.goods.sku;
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.box-show {
|
||||
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.fontFam {
|
||||
/* #ifdef APP-PLUS */
|
||||
font-family: '宋体';
|
||||
/* #endif */
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
<template>
|
||||
<view>
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<!-- 轮播图 -->
|
||||
<block>
|
||||
<cu-swiper indicator-pos="bottomRight" mode="number" height="400" border-radius="0" :list="bannerList"></cu-swiper>
|
||||
</block>
|
||||
<!-- -->
|
||||
<view class="flex items-center justify-between px-30rpx mt-40rpx">
|
||||
<view class="flex items-center justify-center flex-col">
|
||||
<view class="text-32rpx text-txBase text-left w-full">{{ activeObj.name }}</view>
|
||||
<view class="text-28rpx text-txGray mt-15rpx" v-if="activeObj.start_at && activeObj.end_at">{{ activeObj.start_at.substring(0,16) }} - {{ activeObj.end_at.substring(0,16)}}</view>
|
||||
</view>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<view @tap="share" class="bg-hex-e81300 rounded-8rpx px-40rpx h-60rpx leading-60rpx text-white text-30rpx">分享</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button hover-class="none" open-type="share" class="cu-btn bg-hex-e81300 rounded-8rpx px-40rpx h-60rpx leading-60rpx text-white text-30rpx">
|
||||
分享
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
|
||||
<!-- -->
|
||||
<view class="flex justify-center flex-col px-30rpx mt-50rpx text-32rpx">
|
||||
<view>活动商品</view>
|
||||
<view class="mt-3rpx w-130rpx h-6rpx bg-hex-e60012 rounded-xs"></view>
|
||||
</view>
|
||||
<!-- 商品列表 -->
|
||||
<view class="px-rowSm mt-30rpx">
|
||||
<u-waterfall v-model="dataList" ref="uWaterfall">
|
||||
<template v-slot:left="{ leftList }">
|
||||
<view v-for="(item, index) in leftList" :key="index" class="px-rowSm mb-base">
|
||||
<goods :goods="item"></goods>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:right="{ rightList }">
|
||||
<view v-for="(item, index) in rightList" :key="index" class="px-rowSm mb-base">
|
||||
<goods :goods="item"></goods>
|
||||
</view>
|
||||
</template>
|
||||
</u-waterfall>
|
||||
</view>
|
||||
|
||||
<!-- -->
|
||||
<view class="mt-60rpx text-32rpx text-txBase text-left pl-30rpx">活动说明</view>
|
||||
<view class="px-30rpx mt-20rpx">
|
||||
<u-parse :html="activeObj.description"></u-parse>
|
||||
</view>
|
||||
|
||||
<cu-modal
|
||||
v-model="tipShow"
|
||||
title="已结束"
|
||||
confirmText="去逛逛"
|
||||
@confirm="
|
||||
$u.route({
|
||||
url: '/pages/index/index',
|
||||
type: 'switchTab',
|
||||
})
|
||||
"
|
||||
content="该活动已结束"
|
||||
></cu-modal>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import goods from './components/goods.vue';
|
||||
export default {
|
||||
components: {
|
||||
goods,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tipShow:false,
|
||||
isFirstLoading: true,
|
||||
id: '',
|
||||
activeObj: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.getters.user ?? {};
|
||||
},
|
||||
bannerList() {
|
||||
return this.activeObj?.images ?? [];
|
||||
},
|
||||
dataList() {
|
||||
const arr = this.activeObj?.skus ?? []
|
||||
return arr.reduce((pr,cu)=>{
|
||||
cu.is_online && pr.push(cu)
|
||||
return pr
|
||||
},[])
|
||||
},
|
||||
},
|
||||
onLoad({ id }) {
|
||||
this.id = id;
|
||||
this.getDetail();
|
||||
},
|
||||
//微信分享
|
||||
onShareAppMessage(res) {
|
||||
let code = this.user.code ? this.user.code : '';
|
||||
let shareObj = {
|
||||
title: this.activeObj.share_title,
|
||||
path: `/pages/bargain/index?invite_code=${code}&id=${this.id}`,
|
||||
imageUrl: this.activeObj.share_image,
|
||||
};
|
||||
return shareObj;
|
||||
},
|
||||
methods: {
|
||||
async getDetail() {
|
||||
try {
|
||||
const res = await this.$api.get(`/v1/bargains/${this.id}`, {}, { custom: { toast: false } });
|
||||
this.activeObj = res;
|
||||
} catch (err) {
|
||||
this.tipShow = true
|
||||
} finally {
|
||||
this.isFirstLoading = false;
|
||||
}
|
||||
},
|
||||
//分享小程序
|
||||
share() {
|
||||
if (!this.isLogin) return this.$u.routeLogin();
|
||||
let code = this.user.code ? this.user.code : '';
|
||||
uni.share({
|
||||
provider: 'weixin',
|
||||
scene: 'WXSceneSession',
|
||||
type: 5,
|
||||
title: this.activeObj.share_title,
|
||||
imageUrl: this.activeObj.share_image,
|
||||
miniProgram: {
|
||||
id: 'gh_261a68e68e45',
|
||||
path: `/pages/bargain/index?invite_code=${code}&id=${this.id}`,
|
||||
type: 0,
|
||||
webUrl: `https://wap.zichunsheng.cn/register?code=${code}`,
|
||||
},
|
||||
success: (res) => {
|
||||
console.log(res);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err);
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: #ffffff;
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,380 @@
|
|||
<template>
|
||||
<view>
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
<view class="h-400rpx relative">
|
||||
<cu-swiper indicator-pos="bottomRight" mode="number" height="400" border-radius="0" :list="activity_images"></cu-swiper>
|
||||
<!-- 暂时不要 -->
|
||||
<view class="absolute right-40rpx top-50rpx" v-if="false">
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<u-icon name="share" :size="60" @click="shareWchart"></u-icon>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button hover-class="none" open-type="share" class="cu-btn">
|
||||
<u-icon name="share" :size="60" @click="shareWchart"></u-icon>
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white p-20rpx">
|
||||
<view class="text-right text-28rpx text-hex-e60012" v-if="orderDetail.expire_at"
|
||||
>本次砍价结束时间:{{ orderDetail.expire_at.substring(0,16) }}</view
|
||||
>
|
||||
<view class="flex items-center" v-if="!orderDetail.is_owner && !!orderDetail">
|
||||
<u-avatar :size="88" :src="orderDetail.user_avatar"></u-avatar>
|
||||
<text class="text-28rpx ml-20rpx text-28rpx text-hex-3a3a3a">{{ orderDetail.user_nickname }} 邀请您帮TA砍一刀</text>
|
||||
</view>
|
||||
<view class="border border-hex-fcd7ba border-solid p-12px bg-hex-fff9f1 flex mt-20rpx">
|
||||
<view class="flex-none w-206rpx h-206rpx" >
|
||||
<u-image :border-radius="10" width="206rpx" height="206rpx" :src="goods.cover"></u-image>
|
||||
</view>
|
||||
<view class="ml-30rpx flex-1 w-0 flex flex-col">
|
||||
<view class="line-2 h-88rpx text-hex-3a3a3a">{{ goods.name }}</view>
|
||||
<view class="flex items-center flex-1 mt-10rpx">
|
||||
<view class="flex-1 flex flex-col justify-between h-full">
|
||||
<view class="text-hex-e60012 text-40rpx">¥{{ goods.sell_price }}</view>
|
||||
<view class="text-hex-3a3a3a text-28rpx"
|
||||
>最多砍 <text class="text-hex-e60012">{{ goodsDetail.max_bargain_price }}</text
|
||||
>元</view
|
||||
>
|
||||
</view>
|
||||
<view>
|
||||
<view v-if="isBegin" class="bg-hex-f9b076 text-white rounded-8rpx h-60rpx leading-60rpx px-20rpx" @click="$u.routeAuth('/pages/bargain/index',{id:goodsDetail.activity_id})"> 去购买 </view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-40rpx">
|
||||
<view
|
||||
class="w-470rpx mx-auto bg-hex-e60012 rounded-full h-70rpx text-white leading-70rpx text-center text-36rpx"
|
||||
@click="onInviteBargain"
|
||||
v-if="!isBegin"
|
||||
>
|
||||
邀请砍价
|
||||
</view>
|
||||
<block v-else>
|
||||
<view
|
||||
class="w-470rpx mx-auto bg-hex-e60012 rounded-full h-70rpx text-white leading-70rpx text-center text-36rpx"
|
||||
v-if="orderDetail.is_owner"
|
||||
@click="onBuy"
|
||||
>
|
||||
马上购买
|
||||
</view>
|
||||
<block v-else>
|
||||
<view
|
||||
v-if="isLogin"
|
||||
class="w-470rpx mx-auto bg-hex-e60012 rounded-full h-70rpx text-white leading-70rpx text-center text-36rpx"
|
||||
@click="onHelp"
|
||||
>
|
||||
帮朋友砍一刀
|
||||
</view>
|
||||
<block v-else>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button
|
||||
hover-class="none"
|
||||
open-type="getPhoneNumber"
|
||||
@getphonenumber="getPhoneNumber"
|
||||
class="w-470rpx mx-auto bg-hex-e60012 rounded-full h-70rpx text-white leading-70rpx text-center text-36rpx"
|
||||
>
|
||||
帮朋友砍一刀授权
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<view
|
||||
class="w-470rpx mx-auto bg-hex-e60012 rounded-full h-70rpx text-white leading-70rpx text-center text-36rpx"
|
||||
@click="$u.route('/pages/login/index')"
|
||||
>
|
||||
帮朋友砍一刀
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</block>
|
||||
</block>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<view class="mt-20px w-full text-center" v-if="isBegin && orderDetail.is_owner">
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<view class="w-470rpx mx-auto bg-hex-e60012 rounded-full h-70rpx text-white leading-70rpx text-center text-36rpx" @click="shareWchart">分享好友</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button hover-class="none" open-type="share" class="cu-btn w-470rpx mx-auto bg-hex-e60012 rounded-full h-70rpx text-white leading-70rpx text-center text-36rpx">
|
||||
分享好友
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
|
||||
<block v-if="logs.length">
|
||||
<view class="text-center mt-50rpx">
|
||||
<view class="text-center text-hex-eb8d42 title">帮砍好友</view>
|
||||
</view>
|
||||
<view class="mt-20rpx">
|
||||
<view v-for="(item, index) in logs" :key="index" class="mt-20rpx">
|
||||
<view class="border border-hex-fcd7ba border-solid bg-hex-fff9f1 rounded-12rpx p-20rpx flex text-hex-ed9959">
|
||||
<u-avatar :size="88" :src="item.avatar"></u-avatar>
|
||||
<view class="flex-1 ml-20rpx h-full">
|
||||
<view class="h-42rpx">{{ item.nickname }}</view>
|
||||
<view>砍掉了{{ item.bargain_amount }}元</view>
|
||||
</view>
|
||||
<view>{{ item.created_at.substring(0,10) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block v-if="!!qr_code && !orderDetail.is_owner && isBegin">
|
||||
<view class="text-center mt-60rpx">
|
||||
感谢助力,添加客服送您 <text class="text-hex-e60012">5元</text> 现金红包和 <text class="text-hex-e60012">10元</text> 消费抵扣券
|
||||
</view>
|
||||
|
||||
<view class="w-270rpx h-270rpx mx-auto mt-30rpx shadowqr">
|
||||
<u-image width="100%" height="100%" :src="qr_code"></u-image>
|
||||
</view>
|
||||
|
||||
<view class="text-center py-40rpx text-34rpx font-medium"> 截屏扫描二维码 </view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<cu-modal
|
||||
v-model="tipShow"
|
||||
:title="tipObj.title"
|
||||
confirmText="去逛逛"
|
||||
@confirm="
|
||||
$u.route({
|
||||
url: '/pages/index/index',
|
||||
type: 'switchTab',
|
||||
})
|
||||
"
|
||||
:content="tipObj.content"
|
||||
></cu-modal>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from 'vuex';
|
||||
import { getWxCode } from '@/utils/login';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
ads: [],
|
||||
tipShow: false,
|
||||
tipObj: {
|
||||
title: '已结束',
|
||||
content: '该活动已结束',
|
||||
},
|
||||
btnLoading: false,
|
||||
isFirstLoading: true,
|
||||
id: null,
|
||||
skuId: null,
|
||||
orderDetail: {},
|
||||
goodsDetail: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isLogin']),
|
||||
isBegin() {
|
||||
return !!this.orderDetail?.id;
|
||||
},
|
||||
goods() {
|
||||
return this.goodsDetail?.sku ?? {};
|
||||
},
|
||||
logs() {
|
||||
return this.orderDetail?.logs ?? [];
|
||||
},
|
||||
qr_code() {
|
||||
const data = this.ads?.customer_service_qr_code_banner ?? [];
|
||||
return data.length > 0 ? data[0]?.image : '';
|
||||
},
|
||||
activity_images() {
|
||||
return this.goodsDetail?.activity_images ?? [];
|
||||
},
|
||||
user() {
|
||||
return this.$store.getters.user ?? {};
|
||||
},
|
||||
},
|
||||
//微信分享
|
||||
onShareAppMessage(res) {
|
||||
let shareObj = {
|
||||
title: this.goodsDetail?.activity_share_title,
|
||||
path: `/pages/bargain/invite?skuId=${this.skuId}&id=${this.orderDetail.id}`,
|
||||
imageUrl: this.goodsDetail?.activity_share_image ?? '/static/images/share/logs.png',
|
||||
};
|
||||
return shareObj;
|
||||
},
|
||||
async onLoad({ id, skuId }) {
|
||||
this.skuId = skuId;
|
||||
this.id = id;
|
||||
},
|
||||
async onShow(){
|
||||
this.getAds();
|
||||
await this.init();
|
||||
this.getGoodsDetail();
|
||||
},
|
||||
methods: {
|
||||
...mapMutations({
|
||||
LOGIN: 'user/LOGIN',
|
||||
}),
|
||||
async init() {
|
||||
if (this.id) {
|
||||
await this.getDetailByOrder();
|
||||
} else {
|
||||
await this.getDetailOrderBySku();
|
||||
}
|
||||
},
|
||||
shareWchart() {
|
||||
/* #ifdef APP-PLUS */
|
||||
let code = this.user.code ? this.user.code : '';
|
||||
uni.share({
|
||||
provider: 'weixin',
|
||||
scene: 'WXSceneSession',
|
||||
type: 5,
|
||||
title: this.goodsDetail?.activity_share_title,
|
||||
miniProgram: {
|
||||
id: 'gh_261a68e68e45',
|
||||
path: `/pages/bargain/invite?skuId=${this.skuId}&id=${this.orderDetail.id}`,
|
||||
type: 0,
|
||||
webUrl: `https://wap.zichunsheng.cn/register?code=${code}`,
|
||||
},
|
||||
summary: '邀请你砍一刀!!',
|
||||
imageUrl: this.goodsDetail?.activity_share_image ?? '/static/images/share/logs.png',
|
||||
success: (res) => {
|
||||
console.log(res);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err);
|
||||
},
|
||||
});
|
||||
/* #endif */
|
||||
},
|
||||
//去购买
|
||||
onBuy() {
|
||||
const params = {
|
||||
goods: [
|
||||
{
|
||||
sku_id: this.skuId,
|
||||
num: 1,
|
||||
},
|
||||
],
|
||||
type: 'bargains',
|
||||
cuId: this.orderDetail.id,
|
||||
};
|
||||
this.$u.routeAuth('/pages/confirm_order/confirm_order?data=' + encodeURIComponent(JSON.stringify(params)));
|
||||
},
|
||||
async getAds() {
|
||||
const resData = await this.$api.get('/v1/ads', {
|
||||
params: {
|
||||
keys: ['customer_service_qr_code_banner'],
|
||||
},
|
||||
});
|
||||
this.ads = resData;
|
||||
},
|
||||
//根据订单查看
|
||||
async getDetailByOrder() {
|
||||
try {
|
||||
const data = await this.$api.get(`/v1/bargain-order/order/${this.id}`);
|
||||
if (!!data) {
|
||||
this.orderDetail = data;
|
||||
} else {
|
||||
this.tipObj = {
|
||||
title: '已结束',
|
||||
content: '该活动已结束',
|
||||
};
|
||||
this.tipShow = true;
|
||||
this.orderDetail = null
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
//查看自己邀请的人
|
||||
async getDetailOrderBySku() {
|
||||
try {
|
||||
const data = await this.$api.get(`/v1/bargain-order/sku/${this.skuId}`);
|
||||
this.orderDetail = data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
//获取砍价商品
|
||||
async getGoodsDetail() {
|
||||
try {
|
||||
const resData = await this.$api.get(`/v1/bargain-sku/${this.skuId}`);
|
||||
this.goodsDetail = resData;
|
||||
} catch (error) {
|
||||
} finally {
|
||||
this.isFirstLoading = false;
|
||||
}
|
||||
},
|
||||
// 邀请朋友砍价
|
||||
async onInviteBargain() {
|
||||
const resData = await this.$api.post(`/v1/bargains/create-order/${this.skuId}`);
|
||||
this.getDetailOrderBySku();
|
||||
},
|
||||
//帮砍
|
||||
async onHelp() {
|
||||
if (this.btnLoading) return;
|
||||
this.btnLoading = true;
|
||||
try {
|
||||
await this.$api.post(`/v1/bargains/bargain/${this.id}`, {}, { custom: { toast: false } });
|
||||
this.$u.toast('砍价成功');
|
||||
this.init();
|
||||
} catch (error) {
|
||||
this.tipObj = {
|
||||
title: '提示',
|
||||
content: error.message,
|
||||
};
|
||||
this.tipShow = true;
|
||||
} finally {
|
||||
this.btnLoading = false;
|
||||
}
|
||||
},
|
||||
//微信快捷登录
|
||||
async getPhoneNumber(e) {
|
||||
const { encryptedData, iv } = e.detail;
|
||||
if (!iv) return;
|
||||
const invite_code = uni.getStorageSync('INVITE_CODE');
|
||||
const code = await getWxCode();
|
||||
const params = {};
|
||||
if (!!invite_code) params.inviter_code = invite_code;
|
||||
const resData = await this.$api.post('/v1/socialite/code-bind-user/wechat-mini', {
|
||||
type: 'wechat-mini',
|
||||
code,
|
||||
iv,
|
||||
data: encryptedData,
|
||||
...params,
|
||||
});
|
||||
this.LOGIN(resData.token);
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.mx-auto{
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.title {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
&::after,
|
||||
&::before {
|
||||
content: '';
|
||||
height: 1px;
|
||||
width: 14px;
|
||||
background: #eb8d42;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
&::after {
|
||||
right: -30px;
|
||||
}
|
||||
&::before {
|
||||
left: -30px;
|
||||
}
|
||||
}
|
||||
|
||||
.shadowqr {
|
||||
box-shadow: 0px 0px 10px #f9b1767a;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<view class="bg">
|
||||
<!-- 导航栏 -->
|
||||
<u-navbar back-icon-color="#000000" :border-bottom="false">
|
||||
<view slot="right" class="pr-base" @tap="jump">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/cart/short-cart.png" mode="scaleToFill" />
|
||||
</view>
|
||||
</u-navbar>
|
||||
<!-- -->
|
||||
<view class="py-32rpx pl-41rpx text-md text-txGray border-b border-txBorder">全部评价(109)</view>
|
||||
<view v-for="(item,index) in 3" :key="index">
|
||||
<comment :index="index" :length="3"></comment>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import comment from "@/components/comment/comment"
|
||||
export default {
|
||||
components: {
|
||||
comment
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
jump() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/auxiliary_cart/index'
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.bg {
|
||||
background: #FFFFFF;
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,403 @@
|
|||
<template>
|
||||
<view>
|
||||
<!-- 导航栏 -->
|
||||
<u-navbar back-icon-color="#000000" title-color="#000000" :border-bottom="false" title="确认订单">
|
||||
<view slot="right" class="pr-base" @tap="$u.routeAuth('/pages/auxiliary_cart/index')">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/cart/short-cart.png" mode="scaleToFill" />
|
||||
</view>
|
||||
</u-navbar>
|
||||
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
|
||||
<!-- <view v-if="orderInfo.shipping_address">
|
||||
<view class="fixed top-0 left-0 w-full h-full flex items-center justify-center flex-1">
|
||||
<view class="text-center">
|
||||
<view>您还没有收货地址哟!</view>
|
||||
<view
|
||||
class="bg-white border border-solid border-hex-efefef h-60rpx px-40rpx mt-20rpx leading-60rpx rounded-30rpx text-24rpx text-center inline-block"
|
||||
@tap="$u.routeAuth('/pageA/new_address/index', { type: 'select' })"
|
||||
>
|
||||
新建地址
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- -->
|
||||
<view v-if="skuOne.require_shipping_address || type != 'points'" class="flex items-center justify-between px-40rpx bg-white pb-30rpx"
|
||||
@tap="addressAry.length?$u.routeAuth('/pageA/address/index'):$u.routeAuth('/pageA/new_address/index?addressType=select')">
|
||||
<view>
|
||||
<template v-if="orderInfo.shipping_address">
|
||||
<view class="flex items-center text-black">
|
||||
<image v-if="address.is_default" class="w-59rpx" src="/static/images/order/defalut-icon.png"
|
||||
mode="widthFix" />
|
||||
<text>{{ address.zone }}</text>
|
||||
</view>
|
||||
<view class="text-xl text-black my-14rpx"> {{ address.address }} </view>
|
||||
<view class="text-lg text-black"> {{ address.consignee }} {{ address.telephone }} </view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view class="flex items-center text-black">
|
||||
<text>暂无默认地址</text>
|
||||
</view>
|
||||
<view class="text-xl text-black my-14rpx"> 请选择 </view>
|
||||
</template>
|
||||
</view>
|
||||
<view>
|
||||
<u-icon name="arrow-right" color="#808080" size="38"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white rounded-xs mt-24rpx py-base px-40rpx" v-if="goodsList.length">
|
||||
<block v-for="(item, index) in goodsList" :key="index">
|
||||
<view>
|
||||
<view class="flex">
|
||||
<u-image border-radius="10" width="190rpx" height="190rpx" :src="item.sku.cover" :lazy-load="true">
|
||||
</u-image>
|
||||
<view class="flex-1 ml-16rpx w-0 flex justify-between flex-col">
|
||||
<view>
|
||||
<view>
|
||||
<view class="line-2">{{ item.sku.name }}</view>
|
||||
</view>
|
||||
<view class="mt-10rpx">
|
||||
<u-tag v-for="spec in item.sku.specs" :key="spec" size="mini" :text="spec" class="mr-6rpx"
|
||||
shape="square" color="#A6A6A6" bg-color="transparent" mode="dark" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex items-end">
|
||||
<view class="flex-1">
|
||||
<view class="">
|
||||
<view class="flex-1">¥{{ item.sku.sell_price }}</view>
|
||||
</view>
|
||||
<view class="flex items-center" v-if="item.sku.vip_price>0">
|
||||
<view class="">¥{{ item.sku.vip_price }}</view>
|
||||
<image class="w-58rpx h-58rpx ml-10rpx" src="/static/svg/svip.svg" mode=""></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-lg"> x{{ item.quantity }} </view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="my-20rpx" v-if="index != goodsList.length - 1">
|
||||
<u-line color="#E5E5E5" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- -->
|
||||
<view class="bg-white mt-base rounded-xs px-40rpx py-20rpx" v-if="type != 'bargains' && type != 'points'">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="text-md">选择优惠卷</view>
|
||||
<view class="flex items-center" @tap="onOpenCoupon">
|
||||
<view class="text-md text-txGray mr-10rpx">{{ couponShowText }}</view>
|
||||
<u-icon name="arrow-right" color="#808080" size="30"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-gray-400 text-sm">因系统迁移优惠券问题请联系客服</view>
|
||||
</view>
|
||||
<!-- -->
|
||||
<view class="bg-white mt-base rounded-xs pb-base px-40rpx pt-rowSm text-md">
|
||||
<view class="flex items-center justify-between py-rowSm text-txBase" v-if="orderInfo.total_points>0">
|
||||
<view>抵扣积分</view>
|
||||
<view>{{ orderInfo.total_points }}</view>
|
||||
</view>
|
||||
<view class="flex items-center justify-between py-rowSm text-txBase">
|
||||
<view>商品金额</view>
|
||||
<view>¥{{ orderInfo.products_total_amount }}</view>
|
||||
</view>
|
||||
<view class="flex items-center justify-between py-rowSm text-txBase">
|
||||
<view>运费</view>
|
||||
<view>¥{{ orderInfo.shipping_fee }}</view>
|
||||
</view>
|
||||
<view class="flex items-center justify-between py-rowSm text-txBase" v-if="type != 'bargains' && type != 'points'">
|
||||
<view>优惠卷</view>
|
||||
<view class="text-bgSubtitle">-¥{{ orderInfo.coupon_discount_amount }}</view>
|
||||
</view>
|
||||
<view class="flex items-center justify-between py-rowSm text-txBase">
|
||||
<view>会员优惠</view>
|
||||
<view class="text-bgSubtitle">-¥{{ orderInfo.vip_discount_amount }}</view>
|
||||
</view>
|
||||
<view class="flex items-center justify-between py-rowSm text-txBase" v-if="type == 'bargains'">
|
||||
<view>砍价优惠</view>
|
||||
<view class="text-bgSubtitle">-¥{{ orderInfo.bargain_amount }}</view>
|
||||
</view>
|
||||
<!-- -->
|
||||
<view class="text-md border-t border-txBorder flex items-center justify-end py-base">
|
||||
<view class="text-txBase">实付金额:</view>
|
||||
<view class="text-bgSubtitle">¥{{ orderInfo.total_amount }}</view>
|
||||
</view>
|
||||
<!-- -->
|
||||
<view class="border-t border-txBorder pt-base text-md">
|
||||
<textarea :adjust-position="false" v-model="remarks" class="min-h-130rpx w-full" placeholder-style="text-h999"
|
||||
placeholder="订单备注:" maxlength="100" auto-height></textarea>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cu-popup v-model="couponShow" mode="bottom" border-radius="20" closeable close-icon-size="24">
|
||||
<view>
|
||||
<view class="text-center py-30rpx text-lg">优惠券</view>
|
||||
<scroll-view scroll-y="true" class="max-h-900rpx bg-hex-f6f6f6">
|
||||
<coupon-list v-model="tempCouponId" :list="couponsList"></coupon-list>
|
||||
</scroll-view>
|
||||
<view class="p-base">
|
||||
<view class="btn bg-hex-FB4A34 h-84rpx leading-84rpx text-white" @tap="onCounponChange">
|
||||
<text>确定</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</cu-popup>
|
||||
|
||||
<!-- -->
|
||||
<view class="h-130rpx"></view>
|
||||
<!-- -->
|
||||
<view class="flex items-center justify-end bg-white shadow-up fixed right-0 left-0 bottom-0 cu-bar tabbar">
|
||||
<view class="text-md text-txBase">
|
||||
合计: <text class="text-xl" v-if="orderInfo.total_points >0"> {{orderInfo.total_points}}积分+</text> <text class="text-xl text-txSvip">¥{{ orderInfo.total_amount }}</text>
|
||||
</view>
|
||||
<view @tap="createOrder"
|
||||
class="w-180rpx h-66rpx leading-66rpx text-center text-lg ml-40rpx text-white bg-primary rounded-lg mr-base"> 提交订单
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let btnLoading = false;
|
||||
import { add } from '@/utils'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
type: '',
|
||||
goods: [],
|
||||
cards: [],
|
||||
addressId: '',
|
||||
couponId: '',
|
||||
goodsList: [],
|
||||
orderInfo: {},
|
||||
addressList: [],
|
||||
remarks: '',
|
||||
tempCouponId: '',
|
||||
couponShow: false,
|
||||
isFirstLoading:true,
|
||||
addressAry:[],
|
||||
cuId:null
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
const data = JSON.parse(decodeURIComponent(options.data));
|
||||
|
||||
uni.$on('address:select', this.addressUpdate);
|
||||
|
||||
this.type = data.type;
|
||||
this.goods = data?.goods ?? [];
|
||||
this.cards = data?.cards ?? [];
|
||||
this.cuId = data?.cuId
|
||||
},
|
||||
|
||||
computed: {
|
||||
skuOne(){
|
||||
return this.goodsList[0]?.sku ?? {}
|
||||
},
|
||||
//收货地址
|
||||
address() {
|
||||
return this.orderInfo.shipping_address;
|
||||
},
|
||||
//优惠券列表
|
||||
couponsList() {
|
||||
return this.orderInfo?.coupons ?? [];
|
||||
},
|
||||
//优惠券文本显示
|
||||
couponShowText() {
|
||||
if (!!this.couponId) return this.currentCoupon?.name ?? '已过期';
|
||||
if (this.couponsList.length) return `${this.couponsList.length}张可用`;
|
||||
else return '当前无可用';
|
||||
},
|
||||
//当前优惠券
|
||||
currentCoupon() {
|
||||
const coupon = this.couponsList.find((e) => e.id == this.couponId);
|
||||
return coupon;
|
||||
},
|
||||
//配送支持
|
||||
isDistribution() {
|
||||
return this.orderInfo?.shipping_supported ?? false;
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
this.getDetail();
|
||||
this.getAddress()
|
||||
},
|
||||
methods: {
|
||||
//创建订单
|
||||
async createOrder() {
|
||||
const {
|
||||
remarks,
|
||||
addressId,
|
||||
couponId,
|
||||
isDistribution
|
||||
} = this;
|
||||
if(this.type != 'points' || this.skuOne?.require_shipping_address){
|
||||
if (!addressId) return this.$u.toast('收货地址不能为空');
|
||||
if (!isDistribution) return this.$u.toast('所在地区不支持配送');
|
||||
}
|
||||
|
||||
const params = {
|
||||
coupon_id: couponId,
|
||||
shipping_address_id: addressId,
|
||||
note: remarks,
|
||||
};
|
||||
|
||||
if(this.type == 'points'){
|
||||
const {
|
||||
num,
|
||||
sku_id
|
||||
} = this.goods[0];
|
||||
params.sku_id = sku_id
|
||||
params.quantity = num
|
||||
}else if (this.type == 'bargains') {
|
||||
params.cuId = this.cuId
|
||||
} else if (this.type == 'cart') {
|
||||
params.shopping_cart = this.cards;
|
||||
} else {
|
||||
const {
|
||||
num,
|
||||
sku_id
|
||||
} = this.goods[0];
|
||||
params.product = {
|
||||
sku_id: sku_id,
|
||||
quantity: num,
|
||||
};
|
||||
}
|
||||
|
||||
if (btnLoading) return;
|
||||
btnLoading = true;
|
||||
|
||||
try {
|
||||
let order;
|
||||
if(this.type == 'points'){
|
||||
order = await this.$api.post(`/v1/points/orders`, params);
|
||||
}else if(this.type == 'bargains'){
|
||||
order = await this.$api.post(`/v1/bargains/create-mall-order/${this.cuId}`, params);
|
||||
}else{
|
||||
order = await this.$api.post('/v1/order/orders', params);
|
||||
}
|
||||
if(order.status==1 || order.status==2){//支付成功
|
||||
this.$u.routeAuth({
|
||||
url: '/pages/payment_results/payment_results',
|
||||
type: 'redirectTo',
|
||||
params: {
|
||||
id: order.id,
|
||||
type: 'success',
|
||||
},
|
||||
});
|
||||
}else if(order.status==0){ //待付款
|
||||
this.$u.routeAuth({
|
||||
url: '/pages/confirm_payment/confirm_payment',
|
||||
type: 'redirectTo',
|
||||
params: {
|
||||
id: order.id
|
||||
}
|
||||
});
|
||||
}
|
||||
// this.$u.routeAuth({
|
||||
// url: '/pages/confirm_payment/confirm_payment',
|
||||
// type: 'redirectTo',
|
||||
// params: {
|
||||
// id: order.id
|
||||
// }
|
||||
// });
|
||||
} catch (error) {
|
||||
// console.log(error);
|
||||
} finally {
|
||||
btnLoading = false;
|
||||
}
|
||||
},
|
||||
//优惠券改变
|
||||
onCounponChange() {
|
||||
this.couponId = this.tempCouponId;
|
||||
this.getDetail();
|
||||
this.couponShow = false;
|
||||
},
|
||||
onOpenCoupon(){
|
||||
if(this.couponsList.length==0) return this.$u.toast('当前无可用优惠券')
|
||||
this.couponShow = true
|
||||
},
|
||||
//地址更换
|
||||
addressUpdate({
|
||||
id
|
||||
}) {
|
||||
this.addressId = id;
|
||||
},
|
||||
//确认订单详情
|
||||
async getDetail() {
|
||||
const params = {
|
||||
coupon_id: this.couponId,
|
||||
shipping_address_id: this.addressId,
|
||||
};
|
||||
if(this.type == 'points'){
|
||||
const {
|
||||
num,
|
||||
sku_id
|
||||
} = this.goods[0];
|
||||
params.sku_id = sku_id
|
||||
params.quantity= num
|
||||
}else if(this.type == 'bargains'){
|
||||
params.bargain_order_id = this.cuId
|
||||
const {
|
||||
num,
|
||||
sku_id
|
||||
} = this.goods[0];
|
||||
params.product = {
|
||||
sku_id: sku_id,
|
||||
quantity: num,
|
||||
};
|
||||
}else if (this.type == 'cart') {
|
||||
params.shopping_cart = this.cards;
|
||||
} else {
|
||||
const {
|
||||
num,
|
||||
sku_id
|
||||
} = this.goods[0];
|
||||
params.product = {
|
||||
sku_id: sku_id,
|
||||
quantity: num,
|
||||
};
|
||||
}
|
||||
try {
|
||||
let orderInfo
|
||||
if(this.type == 'points'){
|
||||
orderInfo = await this.$api.post('/v1/points/order-check', params);
|
||||
}else{
|
||||
orderInfo = await this.$api.post('/v1/order/verify-order', params);
|
||||
}
|
||||
orderInfo.shipping_address && (this.addressId = orderInfo.shipping_address.id);
|
||||
this.goodsList = orderInfo.products;
|
||||
this.orderInfo = orderInfo;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}finally{
|
||||
this.isFirstLoading = false
|
||||
}
|
||||
},
|
||||
async getAddress(){
|
||||
const resData = await this.$api.get('/v1/shipping-addresses')
|
||||
this.addressAry = resData
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
watch: {
|
||||
couponShow(e) {
|
||||
if (e) this.tempCouponId = this.couponId;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.listChid:last-child {
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
<template>
|
||||
<view>
|
||||
<!-- 导航栏 -->
|
||||
<!-- <u-navbar back-icon-color="#000000" title-color="#000000" title="确认支付">
|
||||
<view slot="right" class="pr-base" @tap="$u.routeAuth('/pages/auxiliary_cart/index')">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/cart/short-cart.png" mode="scaleToFill" />
|
||||
</view>
|
||||
</u-navbar> -->
|
||||
<!-- -->
|
||||
|
||||
<loading-view v-if="isFirstLoading"></loading-view>
|
||||
|
||||
<view class="bg-white h-750rpx rounded-b-xs px-base">
|
||||
<view class="text-xl text-txBase font-extrabold pt-40rpx pl-base">{{ detail.status | payStatusText }}</view>
|
||||
<view class="pl-base text-bgSubtitle text-lg mt-rowLg border-b border-txBorder pb-rowLg"
|
||||
>剩余时间:<u-count-down v-if="expiresAt" :timestamp="expiresAt" font-size="32" color="#f0ad4e" separator-color="#f0ad4e" @end="onCountEnd">
|
||||
</u-count-down>
|
||||
</view>
|
||||
<view class="px-base pt-rowLg text-lg text-txGray">
|
||||
<view class="pt-base">订单编号:{{ detail.sn }}</view>
|
||||
<view class="pt-base" v-if="detail.total_points>0">抵扣积分:{{ detail.total_points }}</view>
|
||||
<view class="pt-base">订单金额:{{ detail.products_total_amount }}元</view>
|
||||
<view class="pt-base" v-if="!!detail.consignee_name">收货人:{{ detail.consignee_name }}</view>
|
||||
<view class="pt-base" v-if="!!detail.consignee_telephone"> 联系方式:{{ detail.consignee_telephone }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white w-710rpx m-auto shdow -mt-130rpx">
|
||||
<radio-group class="" @change="radioChange">
|
||||
<label v-for="(item, index) in payList" :key="index" class="flex items-center px-base py-rowLg border-b border-txBorder">
|
||||
<radio
|
||||
class="mr-base"
|
||||
color="#2ab31c"
|
||||
style="transform: scale(0.8)"
|
||||
:value="String(item.pay_way)"
|
||||
:checked="item.pay_way == payWay"
|
||||
:disabled="item.disabled"
|
||||
></radio>
|
||||
<view class="flex">
|
||||
<view class="text-xl text-txBase">{{ item.name }}</view>
|
||||
<view class="text-xl text-txBase" v-if="item.pay_way == 'balance'">({{ balance.balance }})</view>
|
||||
<view class="text-xl text-txBase" v-if="item.pay_way == 'wallet'">({{ wallet.balance }})</view>
|
||||
</view>
|
||||
</label>
|
||||
</radio-group>
|
||||
</view>
|
||||
<!-- -->
|
||||
<view class="flex items-center justify-end bg-white shadow-up fixed right-0 left-0 bottom-0 cu-bar tabbar">
|
||||
<view class="text-lg text-bgSubtitle mr-rowLg">-¥{{ discountCount }}</view>
|
||||
<view class="text-md text-txBase">
|
||||
合计: <text class="text-xl text-txSvip">¥{{ detail.total_amount }}</text>
|
||||
</view>
|
||||
<view class="w-180rpx h-66rpx leading-66rpx text-center text-lg ml-40rpx text-white bg-primary rounded-lg mr-base" @tap="onPay">确认支付</view>
|
||||
</view>
|
||||
|
||||
<!-- 输入密码 -->
|
||||
<password-popup v-model="passwordShow" @getPassword="onPwdChange">
|
||||
<view v-if="passwordError" class="text-center text-error">密码错误</view>
|
||||
</password-popup>
|
||||
<!-- 没有密码 -->
|
||||
<cu-modal
|
||||
v-model="noPayPwdShow"
|
||||
title="您还未设置支付密码?"
|
||||
:showCancelButton="true"
|
||||
confirmText="去设置"
|
||||
@confirm="$u.routeAuth('/pageA/reset_password/secure')"
|
||||
content="提示:请先设置后在进行操作~"
|
||||
></cu-modal>
|
||||
|
||||
<!-- 放弃支付 -->
|
||||
<cu-modal
|
||||
v-model="giveuUpPayShow"
|
||||
showCancelButton
|
||||
title="确定放弃付款吗?"
|
||||
confirmText="继续支付"
|
||||
cancelText="忍痛离开"
|
||||
@cancel="onBack()"
|
||||
content="好货不等人,请尽快支付~"
|
||||
></cu-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let loadingBtn = false;
|
||||
import { sub } from '@/utils';
|
||||
import { wxpay, alipay } from '@/utils/pay.js';
|
||||
import { payWay as payWayTool } from '@/utils/tools.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isFirstLoading: true,
|
||||
passwordShow: false,
|
||||
noPayPwdShow: false,
|
||||
passwordError: false,
|
||||
giveuUpPayShow: false, // 退出拦截
|
||||
id: '',
|
||||
payData: null,
|
||||
payWayArr: [
|
||||
{
|
||||
name: '微信支付',
|
||||
pay_way: 'wxpay',
|
||||
},
|
||||
// {
|
||||
// name: '支付宝',
|
||||
// pay_way: 'alipay',
|
||||
// },
|
||||
{
|
||||
name: '可提',
|
||||
pay_way: 'wallet',
|
||||
},
|
||||
{
|
||||
name: '余额',
|
||||
pay_way: 'balance',
|
||||
},
|
||||
],
|
||||
payWay: 'wxpay',
|
||||
detail: {},
|
||||
payList: [],
|
||||
};
|
||||
},
|
||||
async onLoad({ id }) {
|
||||
this.id = id;
|
||||
await this.$store.dispatch('app/getConfit');
|
||||
this.$store.dispatch('user/getUserInfo');
|
||||
this.getDetail();
|
||||
this.getPayList();
|
||||
},
|
||||
onBackPress() {
|
||||
if (this.giveuUpPayShow) {
|
||||
return false;
|
||||
}
|
||||
this.giveuUpPayShow = true;
|
||||
return true;
|
||||
},
|
||||
computed: {
|
||||
userInfo() {
|
||||
return this.$store?.getters?.user;
|
||||
},
|
||||
wallet() {
|
||||
return this.userInfo?.wallet ?? {};
|
||||
},
|
||||
balance() {
|
||||
return this.userInfo?.balance ?? {};
|
||||
},
|
||||
discountCount() {
|
||||
return sub(this.detail?.products_total_amount ?? 0, this.detail?.total_amount ?? 0);
|
||||
},
|
||||
payWayShow() {
|
||||
return this.$store.getters.hiddenConfig?.pay_way ?? this.payWayArr.map(({ pay_way }) => pay_way);
|
||||
},
|
||||
expiresAt() {
|
||||
return this.detail?.expires_at ?? 0;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getPayList() {
|
||||
const arr = this.payWayArr.filter(({ pay_way }) => this.payWayShow.findIndex((e) => e == pay_way) >= 0);
|
||||
const list = arr.filter(({ pay_way }) => {
|
||||
let isShow = true;
|
||||
if (pay_way == 'wallet') {
|
||||
if (this.wallet.is_frozen) {
|
||||
isShow = false;
|
||||
} else {
|
||||
isShow = true;
|
||||
}
|
||||
}
|
||||
if (pay_way == 'balance') {
|
||||
if (this.balance.is_frozen) {
|
||||
isShow = false;
|
||||
} else {
|
||||
isShow = true;
|
||||
}
|
||||
}
|
||||
return isShow;
|
||||
});
|
||||
this.payList = list;
|
||||
},
|
||||
//密码确认
|
||||
async onPwdChange(e) {
|
||||
this.passwordShow = false;
|
||||
try {
|
||||
await this.$api.post('/v1/wallet/pay', { ...this.payData, pay_password: e, pay_way: this.payWay }, { custom: { toast: false } });
|
||||
this.getPlayResults('success');
|
||||
} catch (error) {
|
||||
if (error.errcode == 10001) {
|
||||
this.passwordError = true;
|
||||
this.passwordShow = true;
|
||||
} else {
|
||||
this.$u.toast(error.message ?? '系统繁忙,请稍后再试');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//支付结果页面
|
||||
getPlayResults(type) {
|
||||
// this.giveuUpPayShow = true
|
||||
this.$u.routeAuth({
|
||||
url: '/pages/payment_results/payment_results',
|
||||
type: 'redirectTo',
|
||||
params: {
|
||||
id: this.id,
|
||||
type: type,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
radioChange(e) {
|
||||
this.payWay = e.detail.value;
|
||||
},
|
||||
async onPay() {
|
||||
if (loadingBtn) return;
|
||||
loadingBtn = true;
|
||||
try {
|
||||
let payWay = this.payWay;
|
||||
//微信支付
|
||||
if (this.payWay == 'wxpay') {
|
||||
payWay = payWayTool.wxValue;
|
||||
} else if (this.payWay == 'alipay') {
|
||||
payWay = payWayTool.aliValue;
|
||||
} else if (this.payWay == 'wallet') {
|
||||
if (!this.wallet.has_password) return (this.noPayPwdShow = true);
|
||||
if (sub(this.wallet.balance, this.detail.total_amount) < 0) return this.$u.toast('可提不足');
|
||||
} else if (this.payWay == 'balance') {
|
||||
if (!this.wallet.has_password) return (this.noPayPwdShow = true);
|
||||
if (sub(this.balance.balance, this.detail.total_amount) < 0) return this.$u.toast('余额不足');
|
||||
}
|
||||
|
||||
const resData = await this.$api.post(`/v1/order/orders/${this.id}/pay`, {
|
||||
pay_way: payWay,
|
||||
});
|
||||
this.payData = resData.data;
|
||||
if (this.payWay == 'wxpay') {
|
||||
wxpay(resData.data).then((res) => {
|
||||
this.getPlayResults(res);
|
||||
});
|
||||
} else if (this.payWay == 'alipay') {
|
||||
alipay(resData.data).then((res) => {
|
||||
this.getPlayResults(res);
|
||||
});
|
||||
} else if (this.payWay == 'wallet' || this.payWay == 'balance') {
|
||||
this.passwordShow = true;
|
||||
}
|
||||
} catch (error) {
|
||||
// this.getPlayResults('fail');
|
||||
} finally {
|
||||
loadingBtn = false;
|
||||
}
|
||||
},
|
||||
onChangePayWay(e) {
|
||||
this.payWay = e;
|
||||
},
|
||||
async getDetail() {
|
||||
try {
|
||||
const resData = await this.$api.get(`/v1/order/orders/${this.id}`);
|
||||
this.detail = resData;
|
||||
if (this.detail.status != 0) {
|
||||
this.$u.route({ url: `/pages/order_details/index`, type: 'redirectTo', params: { id: this.id } });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
this.isFirstLoading = false;
|
||||
}
|
||||
},
|
||||
async onCountEnd() {
|
||||
this.getDetail();
|
||||
},
|
||||
onBack() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<view class="text-center pt-300rpx">
|
||||
<image class="w-500rpx" src="/static/images/app/devlop.png" mode="widthFix" />
|
||||
</view>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<view class="min-h-full flex flex-col w-full pt-20rpx">
|
||||
<view class="bg-white flex-1">
|
||||
<view class="p-40rpx flex justify-between">
|
||||
<view
|
||||
class="check-button"
|
||||
:class="{ 'bg-primary text-white': checkIndex === index }"
|
||||
v-for="(item, index) in types"
|
||||
:key="index"
|
||||
@tap="checkIndex = index"
|
||||
>{{ item.name }}</view
|
||||
>
|
||||
</view>
|
||||
<!-- 输入框 -->
|
||||
<view class="px-20rpx">
|
||||
<view class="bg-hex-F9F9F9 rounded-20rpx w-full relative">
|
||||
<textarea
|
||||
class="p-30rpx min-h-200rpx"
|
||||
v-model="content"
|
||||
placeholder-style="text-h999"
|
||||
placeholder="为了更好的解决您的问题,请尽量描述详细"
|
||||
maxlength="100"
|
||||
auto-height
|
||||
></textarea>
|
||||
<view class="py-30rpx pl-18rpx pr-100rpx flex flex-wrap relative">
|
||||
<!-- 字数统计 -->
|
||||
<view class="text-h999 absolute right-20rpx bottom-20rpx">{{ content.length }}/100</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 提交 -->
|
||||
<view class="px-30rpx my-200rpx">
|
||||
<view class="login-btn block h-80rpx round bg-orange" @tap="onSubmit">提交</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
types: [{ name: '体验问题' }, { name: '功能问题' }, { name: '其他问题' }],
|
||||
imgList: [],
|
||||
maxImgLength: 5,
|
||||
checkIndex: 0,
|
||||
content: '',
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async onSubmit() {
|
||||
if (!this.content) {
|
||||
return this.$u.toast('请输入您的问题描述信息');
|
||||
}
|
||||
try {
|
||||
await this.$api.post('/v1/user-qr-code', {
|
||||
context: this.content,
|
||||
});
|
||||
this.content=''
|
||||
// uni.navigateBack();
|
||||
this.$u.toast('提交成功');
|
||||
} catch (err) {
|
||||
this.$u.toast(err.message);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.check-button {
|
||||
padding: 0 20rpx;
|
||||
min-width: 186rpx;
|
||||
height: 62rpx;
|
||||
line-height: 62rpx;
|
||||
text-align: center;
|
||||
border-radius: 60rpx;
|
||||
}
|
||||
|
||||
.img {
|
||||
margin-right: 18rpx;
|
||||
margin-top: 18rpx;
|
||||
width: 116rpx;
|
||||
height: 116rpx;
|
||||
color: #999;
|
||||
border-radius: 8rpx;
|
||||
background-color: #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
<template>
|
||||
<view class="py-24rpx">
|
||||
<view class="bg-white py-30rpx px-base">
|
||||
<view class="text-xl flex">
|
||||
<view class="text-hex-808080">承运人:</view>
|
||||
</view>
|
||||
|
||||
<view class="text-xl flex">
|
||||
<view class="text-hex-808080">运单号:</view>
|
||||
<view class="text-black">28137837338292928</view>
|
||||
<view class="bg-hex-E5E5E5 rounded-full h-50rpx text-md px-32rpx leading-50rpx ml-17rpx" @tap="onCopy">复制
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="text-xl flex">
|
||||
<view class="text-hex-808080">预计送达:</view>
|
||||
<view class="text-black">9月14日晚</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white mt-24rpx p-30rpx">
|
||||
<u-time-line class="ml-24rpx">
|
||||
<u-time-line-item nodeTop="2">
|
||||
<template v-slot:node>
|
||||
<view class="u-node">
|
||||
<image class="w-48rpx h-48rpx" src="/static/images/user/user_sign-sign-icon.png" mode="scaleToFill" />
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:content>
|
||||
<view>
|
||||
<view class="text-xl">已签收</view>
|
||||
<view class="text-lg mt-4rpx">您的订单已由快递柜存放。如有疑问您可以联 系配送员,【郭师豪,182928377338】确认。 感谢您,欢迎再次光临。</view>
|
||||
<view class="text-md text-hex-808080 mt-10rpx">2021-02-30 09:15:54</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-time-line-item>
|
||||
<u-time-line-item>
|
||||
<template v-slot:content>
|
||||
<view>
|
||||
<view class="text-xl text-hex-808080">派送中</view>
|
||||
<view class="text-lg mt-4rpx text-hex-808080">您的订单已由快递派送如有疑问您可以联 系配送员,【郭师豪,182928377338】确认。 感谢您,欢迎再次光临。
|
||||
</view>
|
||||
<view class="text-md text-hex-808080 mt-10rpx">2021-02-30 09:15:54</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-time-line-item>
|
||||
<u-time-line-item>
|
||||
<template v-slot:content>
|
||||
<view>
|
||||
<view class="text-xl text-hex-808080">派送中</view>
|
||||
<view class="text-lg mt-4rpx text-hex-808080">您的订单已由快递运输中,请耐心等待,已到 达沈阳市.........</view>
|
||||
<view class="text-md text-hex-808080 mt-10rpx">2021-02-30 09:15:54</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-time-line-item>
|
||||
</u-time-line>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import uniCopy from '@/utils/uni-copy'
|
||||
export default {
|
||||
methods: {
|
||||
onCopy() {
|
||||
uniCopy({
|
||||
content:item.address ?? '',
|
||||
success:(res)=>{
|
||||
this.$u.toast(res);
|
||||
},
|
||||
error:(e)=>{
|
||||
this.$u.toast(e);
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue