6
0
Fork 0

项目初始化

hui.zhou
h30830569 2022-05-09 09:55:36 +08:00
parent cde51de204
commit 4ed701fa0f
311 changed files with 58550 additions and 0 deletions

14
.editorconfig 100644
View File

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

4
.env 100644
View File

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

4
.env.development 100644
View File

@ -0,0 +1,4 @@
ENV = 'development'
VUE_APP_BASE_API = 'https://jiqu.zichunsheng.cn/api'

4
.env.production 100644
View File

@ -0,0 +1,4 @@
ENV = 'production'
VUE_APP_BASE_API = 'https://jiqu.zichunsheng.cn/api'

4
.eslintignore 100644
View File

@ -0,0 +1,4 @@
build/*.js
src/assets
public
dist

198
.eslintrc.js 100644
View File

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

23
.gitignore vendored 100644
View File

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

5
.prettierrc.json 100644
View File

@ -0,0 +1,5 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 150
}

19
README.md 100644
View File

@ -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/).

63
babel.config.js 100644
View File

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

15338
package-lock.json generated 100644

File diff suppressed because it is too large Load Diff

109
package.json 100644
View File

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

29
postcss.config.js 100644
View File

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

28
public/index.html 100644
View File

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

101
src/App.vue 100644
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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对象的属性名在使用 validateresetFields 方法的情况下该属性是必填的
* @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'
},
// inputlabel
label: {
type: String,
default: ''
},
//
prop: {
type: String,
default: ''
},
// 线
borderBottom: {
type: [String, Boolean],
default: ''
},
// labelleft-top-
labelPosition: {
type: String,
default: ''
},
// labelrpx
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, // "*"propsrequiredrules
validateState: '', //
validateMessage: '', //
// message-border-input
errorType: ['message'],
fieldValue: '', // input
// computedthis.parentdata
parentData: {
borderBottom: true,
labelWidth: 90,
labelPosition: 'left',
labelStyle: {},
labelAlign: 'left',
}
};
},
watch: {
validateState(val) {
this.broadcastInputError();
},
// u-formerrorType
"uForm.errorType"(val) {
this.errorType = val;
this.broadcastInputError();
},
},
computed: {
// labelcomputed
uLabelWidth() {
// label('true')labelauto
return this.elLabelPosition == 'left' ? (this.label === 'true' || this.label === '' ? 'auto' : this.$u.addUnit(this
.elLabelWidth)) : '100%';
},
showError() {
return type => {
// errorTypenonetoast
if (this.errorType.indexOf('none') >= 0) return false;
else if (this.errorType.indexOf(type) >= 0) return true;
else return false;
}
},
// label
elLabelWidth() {
// label90使(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() {
// truefalsetrue
this.broadcast('u-input', 'on-form-item-error', this.validateState === 'error' && this.showError('border'));
},
// required
setRules() {
let that = this;
// "*"propsrequiredrules
// u-formu-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-formrulesu-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;
// blurchange
// 使indexOftrigger['blur','change']
// trigger
return rules.filter(res => res.trigger && res.trigger.indexOf(triggerType) !== -1);
},
//
validation(trigger, callback = () => {}) {
//
this.fieldValue = this.parent.model[this.prop];
// blurchange
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) {
// parentDataparentparentData
Object.keys(this.parentData).map(key => {
this.parentData[key] = this.parent[key];
});
// propuForm(u-form-input使uForm)
if (this.prop) {
//
this.parent.fields.push(this);
this.errorType = this.parent.errorType;
//
this.initialValue = this.fieldValue;
// $nextTicku-formrulesref
// $nextTickrefu-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>

View File

@ -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,
},
// labelleft-top-
labelPosition: {
type: String,
default: 'left',
},
// labelrpx
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() {
// formu-form-item
// data
this.fields = [];
},
methods: {
setRules(rules) {
this.rules = rules;
},
// u-form-itemu-form-itemresetField()
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) {
// $nextTickmodel
this.$nextTick(() => {
// form-item
const errorsRes = [];
//
value = [].concat(value);
// fieldsform-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-itemvalidation
field.validation('', (error) => {
// u-form-item
if (error) {
valid = false;
errorArr.push(error);
}
// u-form-itempromisethen
if (++count === this.fields.length) {
resolve(valid); // promisethen
// // 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>

View File

@ -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''falseundefined
this.isError = true;
this.loading = false;
} else {
this.isError = false;
}
}
}
},
computed: {
wrapStyle() {
let style = {};
// addUnit()pxrpx
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');
//
// fadepng
if (!this.fade) return this.removeBgColor();
// opacity1()0()1
this.opacity = 0;
// 00duration()
//
this.durationTime = 0;
// 50msH5
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>

View File

@ -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-modelfalseloading
//
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);
},
// modalv-modelfalsemodal
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>

View File

@ -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: {
// pxrpx
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
},
// fixedfixed
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()thisthis
// bind()thisthis.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>

View File

@ -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
},
// 使Pickerkeyboard
// v-modelprops
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-lefttop-rightbottom-leftbottom-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-topmode=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() {
// valuetruepopup
this.value && this.open();
},
methods: {
// rpx
getUnitValue(val) {
if(/(%|px|rpx|auto)$/.test(val)) return val;
else return val + 'rpx'
},
//
maskClick() {
this.close();
},
close() {
// valuewatchvalueclose
// @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.popupfalsepickeractionsheetpopup
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>

View File

@ -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() {
// columnNum0
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.columnDatacolumnChange
// 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>

View File

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

View File

@ -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',
},
// listrpx
height: {
type: [Number, String],
default: 250,
},
// topLeft|topCenter|topRight|bottomLeft|bottomCenter|bottomRight
indicatorPos: {
type: String,
default: 'bottomCenter',
},
//
effect3d: {
type: Boolean,
default: false,
},
// 3Ditemitemrpx
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: {
// listuCurrent
list(nVal, oVal) {
if (nVal.length !== oVal.length) this.uCurrent = 0;
},
// currentuCurrentcurrentuCurrent
// uCurrent
current(n) {
this.uCurrent = n;
},
},
data() {
return {
uCurrent: this.current, // swiper-itemindex
};
},
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;
},
// uniswipercurrentNumber
elCurrent() {
return Number(this.current);
},
},
methods: {
listClick(index) {
this.$emit('click', index);
},
change(e) {
let current = e.detail.current;
this.uCurrent = current;
// changeindex0
this.$emit('change', current);
},
// animationfinishchange
// swiperuCurrent
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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

36
src/main.js 100644
View File

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

205
src/manifest.json 100644
View File

@ -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" : {
//prompttemplate
"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
}
}
}
}

View File

@ -0,0 +1,13 @@
import { mapGetters } from 'vuex'
export default {
onLoad(option) {
// #ifdef H5
// #endif
},
computed: {
...mapGetters(['isLogin', 'news_num']),
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

626
src/pages.json 100644
View File

@ -0,0 +1,626 @@
{
"pages": [
//pageshttps://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"
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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