Browse Source

vformal 1.1.65 &merge dev clash err

dev
刘嘉炜 3 months ago
parent
commit
7b187d8ce6
  1. 3
      src/App.vue
  2. 5
      src/js/api.js
  3. 12
      src/js/server.js
  4. 3
      src/main.js
  5. 4
      src/manifest.json
  6. 133
      src/pages.json
  7. 377
      src/pages/index/index.vue
  8. 6
      src/pages/login/login.vue
  9. 243
      src/pages/menu/forth.vue
  10. 32
      src/pages/menu/second.vue
  11. 117
      src/pages/menu/third.vue
  12. 4
      src/pages/merchant_login/merchant_login.vue
  13. 3
      src/pages/web_view/web_view.vue
  14. BIN
      src/static/images/icon/arrow_b2.png
  15. BIN
      src/static/images/icon/index/green_bg_circle.png
  16. BIN
      src/static/images/icon/index/tab_12.png
  17. BIN
      src/static/images/icon/index/tab_13.png
  18. BIN
      src/static/images/icon/index/tab_8.png
  19. BIN
      src/static/images/icon/scan_code_btn.png
  20. BIN
      src/static/images/icon/selected_ring.png
  21. BIN
      src/static/images/icon/write_off_fail.png
  22. BIN
      src/static/images/tab/ta_1.png
  23. BIN
      src/static/images/tab/ta_2.png
  24. BIN
      src/static/images/tab/ta_3.png
  25. BIN
      src/static/images/tab/ta_4.png
  26. BIN
      src/static/images/tab/tab_1.png
  27. BIN
      src/static/images/tab/tab_2.png
  28. BIN
      src/static/images/tab/tab_3.png
  29. BIN
      src/static/images/tab/tab_4.png
  30. BIN
      src/static/images/third_pages/banner.png
  31. BIN
      src/static/images/third_pages/bg.png
  32. BIN
      src/static/images/third_pages/tab_a.png
  33. BIN
      src/static/images/third_pages/tab_b.png
  34. 74
      src/store/actions.js
  35. 24
      src/store/index.js
  36. 13
      src/store/mutations.js
  37. 188
      src/subpackage/authorization/components/login.vue
  38. 25
      src/subpackage/authorization/components/user_info/iconfont.css
  39. 133
      src/subpackage/authorization/components/user_info/impower.vue
  40. 357
      src/subpackage/authorization/components/user_info/tuniaoui-wx-user-info.vue
  41. 9
      src/subpackage/authorization/js/api.js
  42. 10
      src/subpackage/authorization/js/server.js
  43. 16
      src/subpackage/authorization/pages/index.vue
  44. 0
      src/subpackage/authorization/static/images/author_modal.png
  45. 41
      src/subpackage/menu/components/bottom_logo.vue
  46. 87
      src/subpackage/menu/components/mine/header.vue
  47. 63
      src/subpackage/menu/components/mine/line_tab.vue
  48. 16
      src/subpackage/menu/pages/index.vue
  49. BIN
      src/subpackage/menu/static/images/arrow_b2.png
  50. BIN
      src/subpackage/menu/static/images/bot_logo.png
  51. BIN
      src/subpackage/menu/static/images/mine_tab/0.png
  52. BIN
      src/subpackage/menu/static/images/mine_tab/1.png
  53. BIN
      src/subpackage/menu/static/images/mine_tab/2.png
  54. BIN
      src/subpackage/menu/static/images/mine_tab/3.png
  55. BIN
      src/subpackage/menu/static/images/mine_tab/4.png
  56. BIN
      src/subpackage/menu/static/images/mine_tab/5.png
  57. BIN
      src/subpackage/menu/static/images/mine_tab/6.png
  58. 59
      src/subpackage/message/components/detail/answer_item.vue
  59. 51
      src/subpackage/message/components/detail/image_flow.vue
  60. 60
      src/subpackage/message/components/detail/info.vue
  61. 37
      src/subpackage/message/components/edit/fixed_button.vue
  62. 195
      src/subpackage/message/components/edit/info_edit.vue
  63. 189
      src/subpackage/message/components/edit/upload_img.vue
  64. 33
      src/subpackage/message/components/fixed_button.vue
  65. 85
      src/subpackage/message/components/message_item.vue
  66. 16
      src/subpackage/message/js/api.js
  67. 9
      src/subpackage/message/js/server.js
  68. 171
      src/subpackage/message/pages/complaint/detail.vue
  69. 87
      src/subpackage/message/pages/complaint/edit.vue
  70. 105
      src/subpackage/message/pages/complaint/list.vue
  71. 171
      src/subpackage/message/pages/work_order/detail.vue
  72. 99
      src/subpackage/message/pages/work_order/edit.vue
  73. 105
      src/subpackage/message/pages/work_order/list.vue
  74. BIN
      src/subpackage/message/static/images/arrow_b2.png
  75. BIN
      src/subpackage/message/static/images/close.png
  76. BIN
      src/subpackage/message/static/images/message.png
  77. BIN
      src/subpackage/message/static/images/unfold.png
  78. 14
      src/subpackage/verification/pages/record.vue
  79. 4
      src/subpackage/wallet/pages/index/index.vue
  80. 12
      src/uni.scss
  81. 8
      src/utils/util.js

3
src/App.vue

@ -3,9 +3,8 @@
import util from './utils/util';
export default {
onLaunch: function() {
// #ifndef H5
// this.$store.commit('setLoginState', { loginState: false });
this.updateManager();
// #endif
},
methods: {
isLogin(){

5
src/js/api.js

@ -167,5 +167,10 @@ API['party'] = {
bindingOpenId: `${ORIGIN}/assistant/bindingOpenId`, //改为小程序端绑定openid
}
API['mine'] = {
userCurrent:`${ORIGIN}/user/current`, //获取账户信息, (不要传品牌id)
unbindAssistant:`${ORIGIN}/admin/user/unbindAssistant`, // 解绑退出
}
export default { ORIGIN, API };

12
src/js/server.js

@ -1,5 +1,6 @@
import util from '../utils/util';
// import { app as vm } from '../main';
import { app as vm } from '../main';
const islog = true;
@ -27,6 +28,9 @@ export class Server {
method: method,
data: res,
})
handleUserExceptions({ res });
if(isDefaultGet){
if(failMsg == '')throw Error('默认回调,失败提示不能为空 key -> failMsg');
defaultGet({
@ -70,6 +74,14 @@ export class Server {
reject({url,res,data});
}
}
// 用户信息异常
function handleUserExceptions({ res }){
if(res?.data?.code === 401&&res?.data?.message?.indexOf('用户') !== -1){
console.log('用户信息异常,请重新登录');
const _store = vm.$store;
_store.commit('setLoginState', { loginState: false });
}
}
}
get({url,data={},header={},isDefaultGet=true,failMsg=''}){
return this.request(url,data,'GET',header,isDefaultGet,failMsg);

3
src/main.js

@ -6,8 +6,9 @@ Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
export const app = new Vue({
...App,
store
})
app.$mount()

4
src/manifest.json

@ -76,7 +76,9 @@
"appid" : "wxf1294b279ad1b845",
"setting" : {
"urlCheck" : false,
"minified" : true
"minified" : true,
"ignoreDevUnusedFiles": false,
"ignoreUploadUnusedFiles": false
},
"usingComponents" : true,
"optimization": {

133
src/pages.json

@ -1,12 +1,47 @@
{
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"componentPlaceholder": {
"bottom-logo": "view",
"authorization-login": "view"
}
}
},
{
"path": "pages/menu/second",
"style": {
"navigationBarTitleText": "推广营销",
"componentPlaceholder": {
"bottom-logo": "view"
}
}
},
{
"path": "pages/menu/third",
"style": {
"navigationBarTitleText": "商家服务",
"componentPlaceholder": {
"bottom-logo": "view"
}
}
},
{
"path": "pages/menu/forth",
"style": {
"navigationBarTitleText": "个人中心",
"componentPlaceholder": {
"mine-header": "view",
"bottom-logo": "view",
"line-tab": "view",
"authorization-login": "view",
"authorization-user": "view"
}
}
},
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录"
@ -895,6 +930,71 @@
}
}
]
},
{
"root": "subpackage/message",
"pages": [
{
"path": "pages/complaint/edit",
"style" : {
"navigationBarTitleText": "投诉建议"
}
},
{
"path": "pages/complaint/list",
"style" : {
"enablePullDownRefresh" : true,
"navigationBarTitleText": "投诉建议"
}
},
{
"path": "pages/complaint/detail",
"style" : {
"navigationBarTitleText": "投诉详情"
}
},
{
"path": "pages/work_order/list",
"style" : {
"enablePullDownRefresh" : true,
"navigationBarTitleText": "工单列表"
}
},
{
"path": "pages/work_order/detail",
"style" : {
"navigationBarTitleText": "工单详情"
}
},
{
"path": "pages/work_order/edit",
"style" : {
"navigationBarTitleText": "提交工单"
}
}
]
},
{
"root": "subpackage/menu",
"pages": [
{
"path": "pages/index",
"style" : {
"navigationBarTitleText": ""
}
}
]
},
{
"root": "subpackage/authorization",
"pages": [
{
"path": "pages/index",
"style" : {
"navigationBarTitleText": ""
}
}
]
}
],
"globalStyle": {
@ -902,5 +1002,36 @@
"navigationBarTitleText": "欧轩智能商家助手",
"navigationBarBackgroundColor": "#009874",
"backgroundColor": "#f2f2f7"
},
"tabBar": {
"color": "#B2B2B2",
"selectedColor": "#009874",
"backgroundColor": "#FFFFFF",
"list": [
{
"pagePath": "pages/index/index",
"text": "工作台",
"iconPath": "static/images/tab/tab_1.png",
"selectedIconPath": "static/images/tab/ta_1.png"
},
{
"pagePath": "pages/menu/second",
"text": "推广营销",
"iconPath": "static/images/tab/tab_2.png",
"selectedIconPath": "static/images/tab/ta_2.png"
},
{
"pagePath": "pages/menu/third",
"text": "商家服务",
"iconPath": "static/images/tab/tab_3.png",
"selectedIconPath": "static/images/tab/ta_3.png"
},
{
"pagePath": "pages/menu/forth",
"text": "我的",
"iconPath": "static/images/tab/tab_4.png",
"selectedIconPath": "static/images/tab/ta_4.png"
}
]
}
}

377
src/pages/index/index.vue

@ -2,22 +2,22 @@
<view class="index-container">
<view class="ic-content">
<view class="ic-header">
<view class="ih-address" v-if="loginStatus" @click="toStoreList">
<view class="ih-address" v-if="loginState" @click="toStoreList">
<view>{{indexData.brand_name || '-'}}({{indexData.stadium_num || '0'}})</view>
<image src="/static/images/icon/arrow_ff.png" mode="aspectFit"></image>
</view>
<view v-else class="ih-btn" hover-class="hover-active" @click="showAuthor">点击登陆</view>
<block v-if="isPermissionShowTab({ serverKey: 1001 })">
<view class="ih-tip">今日总收入</view>
<view class="ih-price"><text>{{loginStatus?'¥':''}}</text>{{loginStatus?(indexData.amount || '0'):'***'}}</view>
<view class="ih-price"><text>{{loginState?'¥':''}}</text>{{loginState?(indexData.amount || '0'):'***'}}</view>
<view class="ih-amount">
<view>
<view>收款笔数</view>
<view>{{loginStatus?(indexData.in_count || '0'):'**'}}</view>
<view>{{loginState?(indexData.in_count || '0'):'**'}}</view>
</view>
<view>
<view>退款笔数</view>
<view>{{loginStatus?(indexData.out_count || '0'):'**'}}</view>
<view>{{loginState?(indexData.out_count || '0'):'**'}}</view>
</view>
</view>
</block>
@ -37,7 +37,8 @@
</view>
<view class="ic-tabs" v-if="indexData&&indexData.permission">
<block v-for="(e, i) in tabList" :key="i">
<view class="it-item" @click="toPageInfo(e)" v-if="isPermissionShowTab(e)">
<!-- tid1807 去掉"核销查询菜单(已有悬浮按钮) -->
<view class="it-item" @click="toPageInfo(e)" v-if="isPermissionShowTab(e)&&e.id!=4">
<view>
<image mode="aspectFit" :src="'/static/images/icon/index/tab_'+ e.id + '.png'"></image>
<view>{{e.name}}</view>
@ -49,33 +50,12 @@
<navigator url="/subpackage/party/pages/login/login">party/pages/login/login</navigator>
</view> -->
</view>
<view class="ox-dark-mask" v-if="isShowAuthorModal">
<view class="ic-author-modal">
<view class="iam-title">微信授权</view>
<view class="iam-tip">您的信息和数据将受到保护</view>
<image class="iam-pic" mode="aspectFit" src="/static/images/icon/author_modal.png"></image>
<view class="iam-btns">
<button plain hover-class="hover-active" @click="cancelAuthor">取消</button>
<bottom-logo></bottom-logo>
<button
v-if="isProfile"
plain
hover-class="hover-active"
@click="profileConfirm"
>授权并登录</button>
<button
v-else
plain
hover-class="hover-active"
open-type="getUserInfo"
lang="zh_CN"
@getuserinfo="confirmAuthor"
>授权并登录</button>
</view>
</view>
</view>
<authorization-login ref="authorizationLogin"></authorization-login>
<view class="fly_btn" @click="toPageInfo(tabList[4])" v-if="loginStatus">
<view class="fly_btn" @click="toPageInfo(getTabForID(4))" v-if="loginState&&isPermissionShowTab(getTabForID(4))">
<image class="f_bg" src="/static/images/icon/index/green_bg_circle.png" mode="scaleToFill"/>
<image class="f_icon" src="/static/images/icon/index/scan_icon_white.png" mode="scaleToFill"/>
<text>核销</text>
@ -88,6 +68,19 @@
import util from '../../utils/util';
import { servers } from '../../js/server';
import { API } from '../../js/api';
import { mapGetters } from 'vuex';
import bottomLogo from "@/subpackage/menu/components/bottom_logo.vue";
import authorizationLogin from "@/subpackage/authorization/components/login.vue";
/**
* tid1807
* 1去掉"核销查询菜单(已有悬浮按钮) *
* 2去掉收款记录(在收入统计页面已有入口进入收款记录) *
* 3去掉进场人数异常(该入口计划放入新改版的核销查询页面) *
* 4系统工具文案改为小程序管理;钱包提现文案改为钱包&提现: 设备管理文案改为智能设备; *
* 5增加底部 (工作台推广营销商家服务我的) *
* 6增加工单功能
*
* */
const tabList = [
{
id: 0,
@ -96,16 +89,10 @@
serverKey: 1001 //
},
{
id: 1,
name: '收款记录',
path: '/subpackage/income/pages/details_record/details_record',
serverKey: 1002 //
},
{
id: 2,
name: '订单管理',
path: '/subpackage/order/pages/order_manage/order_manage',
serverKey: 1012 //
id: 11,
name: '钱包&提现',
path: '/subpackage/wallet/pages/index/index',
serverKey: 1017 // 1017
},
{
id: 3,
@ -114,10 +101,10 @@
serverKey: 1007 //
},
{
id: 4,
name: '核销查询',
path: '/subpackage/verification/pages/index',
serverKey: 1008 //
id: 2,
name: '订单管理',
path: '/subpackage/order/pages/order_manage/order_manage',
serverKey: 1012 //
},
{
id: 5,
@ -127,23 +114,11 @@
serverKey: 1009 //
},
{
id: 6,
name: '设备管理',
path: '/subpackage/device/pages/index/index',
serverKey: 1010 //
},
{
id: 7,
name: '商品零售',
path: '/subpackage/retail/pages/index/index',
serverKey: 1011 //
},
// {
// id: 8,
// name: '',
// path: '/subpackage/course/pages/index/index',
// serverKey: 1013 // 1013
// },
{
id: 9,
name: '储值卡管理',
@ -151,79 +126,85 @@
serverKey: 1014 // 1014
},
{
id: 10,
name: '进场人数异常',
path: '/subpackage/blacklist/pages/abnormal_list/abnormal_list',
serverKey: 1015 // 1015
},
{
id: 11,
name: '钱包提现',
path: '/subpackage/wallet/pages/index/index',
serverKey: 1017 // 1017
id: 6,
name: '智能设备',
path: '/subpackage/device/pages/index/index',
serverKey: 1010 //
},
{//ICON
id: 12,
name: '系统工具',
name: '小程序管理',
path: '/subpackage/party/pages/index/index',
serverKey: 1016 // 1016
},
{
id: 13,
name: '工单',
path: '/subpackage/message/pages/work_order/list',
serverKey: 1022 // 1022
},
// {
// id: 1,
// name: '',
// path: '/subpackage/income/pages/details_record/details_record',
// serverKey: 1002 //
// },
{
id: 4,
name: '核销查询',
path: '/subpackage/verification/pages/index',
serverKey: 1008 //
},
// {
// id: 8,
// name: '',
// path: '/subpackage/course/pages/index/index',
// serverKey: 1013 // 1013
// },
// {
// id: 10,
// name: '',
// path: '/subpackage/blacklist/pages/abnormal_list/abnormal_list',
// serverKey: 1015 // 1015
// },
];
const app = getApp();
// #ifndef H5
const uniGetSetting = util.promisify(uni.getSetting);
const uniLogin = util.promisify(uni.login);
const uniGetUserInfo= util.promisify(uni.getUserInfo);
const APPID = uni.getAccountInfoSync().miniProgram.appId;
// #endif
// #ifdef H5
const APPID = "wx7106e84614cf0060" //TODO
// #endif
export default {
components: { bottomLogo, authorizationLogin },
computed: {
isProfile: _=>util.isProfile(),
// loginStatus(){
// return app.isLogin();
// }
...mapGetters([ 'loginState' ]),
},
data() {
return {
tabList,
isShowAuthorModal: false,
indexData: {},
loginStatus: false,
}
},
async onLoad(options) {
try{
util.showLoad();
let _loginRes = {};
// 20230829
if(options.role !== 'ADMIN-PLATFORM')await this.checkUserAuthor();
util.hideLoad();
this.loginStatus = app.isLogin();
if(!!app.isLogin())this.getIndexInfo();
if(options.role !== 'ADMIN-PLATFORM')_loginRes = await this.$store.dispatch('checkUserAuthor');
// 20240325 openid
if(_loginRes?.data === '')this.webBrandUserCheck();
if(_loginRes?.data)this.getIndexInfo();
}catch(err){
util.hideLoad();
uni.removeStorageSync('token');
// util.showNone(err.message || '');
console.log('onLoad err',err);
this.loginStatus = app.isLogin();
console.warn('pages index onLoad err--->', err);
}
},
onShow(){
let { indexData } = this;
if(JSON.stringify(indexData)!='{}'&&!!app.isLogin())this.getIndexInfo();
let { indexData, loginState, getIndexInfo } = this;
if(JSON.stringify(indexData)!='{}'&&loginState)getIndexInfo();
},
methods: {
getTabForID(id){
return tabList.find(e=>e.id === id) || {}
},
// ID1000840
isPermissionShowTab(e){
let { indexData } = this;
@ -233,13 +214,13 @@
return false;
},
toNoticeList(){
if(!app.isLogin())return this.showAuthor();
if(!this.loginState)return this.showAuthor();
util.routeTo(`/pages/message/list/list`,'nT');
},
toPageInfo(tabInfo){
let { indexData } = this;
let { indexData, loginState } = this;
let _permission = indexData.permission || {};
if(!app.isLogin())return this.showAuthor();
if(!loginState)return this.showAuthor();
if(!tabInfo.path)return util.showNone('暂未开放!');
//if(tabInfo.id == 2)return util.routeTo(tabInfo.path,'nT'); //
if(!_permission[tabInfo.serverKey])return util.showNone('暂无权限,请联系管理员开启!')
@ -251,10 +232,9 @@
});
}
if([ 10, 11, 4 ].includes(tabInfo.id))return util.routeTo(tabInfo.path + `?brand_id=${indexData.brand.id}`,'nT');
if([ 10, 11, 4, 13 ].includes(tabInfo.id))return util.routeTo(tabInfo.path + `?brand_id=${indexData.brand.id}`,'nT');
if(tabInfo.id === 12){
// if(!indexData.brand.mini_wechat_appid)return util.showNone('appid')
let url = tabInfo.path + `?appid=${indexData.brand.mini_wechat_appid}`
console.log(222,url)
return util.routeTo(url,'nT');
@ -275,137 +255,15 @@
this.$store.commit('setBrandInfo',res);
})
},
// token
async checkUserAuthor(){
try{
let loginRes =""
// #ifndef H5
loginRes = await uniLogin();
// #endif
// #ifdef H5
loginRes = {errMsg: "login:ok", code: "0c3xEi0w3kr1t23zcU3w3ZQR3w3xEi0h"} //TODO H5
// #endif
console.log(loginRes)
return servers.get({
url: API.wechatMiniAppLoginAndSync,
data: {
code: loginRes.code,
appid: APPID,
// #ifdef H5
token:"3d2b0092-e761-11ee-8a66-5254005df464", //TODO
// #endif
},
isDefaultGet: false,
})
.then(res=>{
util.hideLoad();
let _data = res.data;
if(_data.code == 0){
// let _data = res.data.data;
if(_data.data == ''){
uni.removeStorageSync('token');
this.loginStatus = app.isLogin();
// 20240325 openid
this.webBrandUserCheck();
// return util.routeTo(`/pages/login/login`,'rL');
}
uni.setStorageSync('token',_data.data);
return _data;
}else{
util.showNone(_data.message || '校验身份失败!');
throw res.data || {};
}
})
.catch(err=>{
throw err;
})
}catch(err){
throw err;
}
},
showAuthor(){
this.isShowAuthorModal = true
},
closeAuthor(){
this.isShowAuthorModal = false
},
cancelAuthor(){
this.closeAuthor();
},
getLoginQuery({
userInfo,
loginRes
}){
return {
appid: APPID,
code: loginRes.code,
encryptedData: userInfo.encryptedData,
// is_details: 1,
//
user_info: userInfo.userInfo,
user_raw_data: userInfo.rawData,
...userInfo.userInfo,
}
},
//
profileConfirm(){
uni.getUserProfile({
lang: 'zh_CN', desc: '授权登陆',
success: res => {
this.confirmAuthor({detail: {...res}});
this.$refs?.authorizationLogin?.alert?.({
success: ()=>{
setTimeout(this.getIndexInfo, 1000);
},
fail: function(err) {
util.showNone('获取用户信息失败!请重试');
console.warn('getUserProfile Err', err)
fail: err =>{
// console.warn('pages index showAuthor authorizationLogin Err ->', err)
}
})
},
async confirmAuthor(userRes){
if(!userRes.detail.userInfo){
this.closeAuthor();
return util.showNone('获取用户信息失败!请稍后重试');
}
let loginRes = await uniLogin();
if(!loginRes.code){
this.closeAuthor();
return util.showNone('获取登陆凭证失败!稍后重试');
}
servers.post({
url: API.wechatMiniAppLoginAndSync,
data: this.getLoginQuery({
userInfo: userRes.detail,
loginRes
}),
isDefaultGet: false,
})
.then(res=>{
util.hideLoad();
let _data = res.data || {};
if(_data.code == 0){
if(_data.data == '')return util.routeTo(`/pages/login/login`,'rL');
util.showNone(_data.message || '登陆成功!');
// let _data = res.data.data;
// if(_data.user.role == '')return util.routeTo(`/pages/merchant_login/merchant_login`,'rL');
uni.setStorageSync('token',_data.data);
setTimeout(_=>{
this.getIndexInfo();
this.closeAuthor();
this.loginStatus = app.isLogin();
}, 1200);
}else{
util.showNone(_data.message || '后台登陆失败!');
setTimeout(_=>this.closeAuthor(), 1200);
}
}).catch(util.hideLoad)
},
webBrandUserCheck(){
@ -451,8 +309,8 @@
})
},
toStoreList(){
if(!app.isLogin())return this.showAuthor();
let { indexData } = this;
let { indexData, loginState } = this;
if(!loginState)return this.showAuthor();
util.routeTo(`/pages/store_list/store_list?brand_id=${indexData.brand.id}`,'nT');
},
bindUserOpenid(){
@ -682,57 +540,6 @@
}
}
.ic-author-modal{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding-top: 60upx;
width: 662upx;
height: 884upx;
border-radius: 10upx;
background-color: #fff;
.iam-title{
margin-bottom: 22upx;
text-align: center;
line-height: 60upx;
font-size: 44upx;
font-weight: 500;
color: #1a1a1a;
}
.iam-tip{
margin-bottom: 52upx;
line-height: 40upx;
text-align: center;
font-size: 28upx;
color: #9c9c9f;
}
.iam-pic{
margin: 0 auto 62upx;
display: block;
width: 488upx;
height: 416upx;
}
.iam-btns{
@include centerFlex(center);
>button{
margin: 0 20upx;
width: 240upx;
height: 92upx;
line-height: 88upx;
text-align: center;
border-radius: 46upx;
border: 2upx solid $themeColor;
font-size: 32upx;
color: $themeColor;
&+button{
background-color: $themeColor;
color: #fff;
}
}
}
}
// fly_btn
.fly_btn{

6
src/pages/login/login.vue

@ -43,7 +43,7 @@
<script>
import { API } from '../../js/api';
import { servers } from '../../js/server';
import util from '../../utils/util';
import util, { jsonStr } from '../../utils/util';
// import wxplugin from '../../utils/wx_plugin';
const uniGetSetting = util.promisify(uni.getSetting);
const uniLogin = util.promisify(uni.login);
@ -67,7 +67,7 @@ export default {
//
MiniprogramThirdpartyPlugin.init(wx)
// MiniprogramThirdpartyPlugin.init(wx)
},
@ -189,7 +189,7 @@ export default {
}
},
toWebView(){
util.routeTo(`/pages/web_view/web_view?src=${encodeURIComponent("https://www.ouxuanzhineng.cn")}`,'rL');
util.routeTo(`/pages/web_view/web_view?src=${jsonStr("https://www.ouxuanzhineng.cn")}`,'rL');
}
},
}

243
src/pages/menu/forth.vue

@ -0,0 +1,243 @@
<template>
<view class="forth-menu">
<!-- 后端 account-> 如果有手机号就显示手机号 没有的话显示username, 都没有的话就空好了 -->
<mine-header
:is-login="loginState"
:nickname="userInfo.nickname"
:name="userInfo.actual_name"
:account="userInfo.mobile || userInfo.username || ''"
:photo="userInfo.avatar_url"
@on:munted="menuPackageLoaded = true"
@click:login="loginBtn"
@click:update="updateUser"
></mine-header>
<block v-if="menuPackageLoaded">
<line-tab :icon-num='0'>
<template slot="default">账号管理</template>
<template slot="right">
<view class="ft-account">
<view class="fc-name" v-if="loginState">{{ accountName || '-' }}</view>
<image class="fc-icon" mode="aspectFit" src="/static/images/icon/arrow_b2.png"></image>
</view>
</template>
</line-tab>
<line-tab :icon-num='1' @click="toWebView(deadData.helpCenterLink)">帮助中心</line-tab>
<block v-if="loginState">
<line-tab :icon-num='2' @click="toMiniProgram(deadData.assistantCoachAppid)">教练助手</line-tab>
<line-tab :icon-num='3'>
<template slot="default">收银系统</template>
<template slot="right">
<view class="fm-copy" @click="copyLink(deadData.cashierSystemLink)">(复制网址)</view>
</template>
</line-tab>
<line-tab :icon-num='4' @click="toWebView(deadData.backstageLink)">
<view class="fm-admin">
<view class="fa-txt">总后台<text class="ft-txt">({{ deadData.backstageLink }})</text></view><view class="fa-copy" @click.stop="copyLink(deadData.backstageLink)">复制</view>
</view>
</line-tab>
</block>
<line-tab :icon-num='5' @click="toComplaint">投诉建议</line-tab>
<line-tab :icon-num='6' v-if="loginState" @click="unBindBtn">解绑退出</line-tab>
</block>
<bottom-logo></bottom-logo>
<authorization-login ref="authorizationLogin"></authorization-login>
<authorization-user ref="authorizationUser"></authorization-user>
</view>
</template>
<script>
import mineHeader from "@/subpackage/menu/components/mine/header.vue";
import lineTab from "@/subpackage/menu/components/mine/line_tab.vue";
import bottomLogo from "@/subpackage/menu/components/bottom_logo.vue";
import authorizationLogin from "@/subpackage/authorization/components/login.vue";
import authorizationUser from "@/subpackage/authorization/components/user_info/impower.vue";
import { routeTo, debounce, showLoad, hideLoad, showModal, showNone, jsonStr } from "@/utils/util.js";
import { mapGetters, mapState } from 'vuex';
import { servers } from '../../js/server';
import { API } from '../../js/api';
export default {
components:{
mineHeader,
lineTab,
bottomLogo,
authorizationLogin,
authorizationUser,
},
data(){
return {
//
menuPackageLoaded: false,
userInfo: {},
deadData: {
helpCenterLink: 'https://help.ouxuanzhineng.cn/', //
cashierSystemLink: 'https://kb.ouxuanzhineng.cn/', //
backstageLink: 'https://admin.ouxuanzhineng.cn/', //
assistantCoachAppid: 'wxd71043ec955dfecf', // AppID
},
}
},
computed: {
...mapGetters([ 'loginState' ]),
...mapState([ 'brandInfo' ]),
extension(){
return this.userInfo?.extension || {}
},
/**
* @param {string} permission_type 1:ADMIN-STADIUM 2:ADMIN-BRAND
* */
permissionType(){
return this.userInfo?.extension?.permission_type || '';
},
accountName(){
let { permissionType, extension } = this;
if(permissionType === 'ADMIN-STADIUM')return extension?.stadium_name ?? '';
if(permissionType === 'ADMIN-BRAND')return extension?.brand_name ?? '';
return '';
}
},
onLoad(options){
if(this.loginState)this.getUserInfo();
},
methods: {
//
unBindBtn: debounce(function(){
let { userInfo } = this;
if(!userInfo?.id)return showModal({ content: '用户信息有误' })
showModal({
content: '您确定要解绑退出吗?',
showCancel: true,
success: mRes=>{
if(mRes.confirm)this.unBindReq(userInfo.id);
}
})
}, 300, true),
//
unBindReq(user_id){
showLoad();
return servers.post({
url: API.mine.unbindAssistant,
data: { user_id },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
showNone('操作成功!');
setTimeout(() => {
this.$store.commit('setLoginState', { loginState: false });
routeTo(`/pages/login/login`,'rL');
}, 1000);
console.log('pages menu unBindReq res --->', _data);
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({ content: err?.message || '操作失败!' });
console.warn('pages menu unBindReq err --->', err);
// return Promise.reject(err);
})
},
//
updateUser(){
this.$refs?.authorizationUser?.show?.({ success: this.getUserInfo });
},
//
toMiniProgram(appid){
uni.navigateToMiniProgram({ appId: appid });
},
//
toWebView(url){
routeTo(`/pages/web_view/web_view?src=${jsonStr(url)}`, 'nT');
},
//
copyLink(url){
uni.setClipboardData({ data: url });
},
//
toComplaint(){
let { loginState, showAuthor, brandInfo } = this;
if(!loginState)return showAuthor();
routeTo(`/subpackage/message/pages/complaint/list?brand_id=${brandInfo?.brand?.id}`, 'nT');
},
//
loginBtn(){
this.showAuthor();
},
showAuthor(){
this.$refs?.authorizationLogin?.alert?.({ success: this.getUserInfo });
},
getUserInfo(){
showLoad();
return servers.post({
url: API.mine.userCurrent,
data: {},
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
this.userInfo = _data?.data || {};
console.log('pages menu getUserInfo res --->', _data);
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({ content: err?.message || '加载用户失败!' });
console.warn('pages menu getUserInfo err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.forth-menu{
}
.fm-txt{
font-size: 28upx;
.ft-copy{
color: $mColor;
}
}
.ft-account{
@include ctf(flex-end);
.fc-name{
@include flcw(32upx, 48upx, #9A9A9D);
@include tHide;
}
.fc-icon{
flex-shrink: 0;
margin-left: 22upx;
width: 30upx;
height: 30upx;
}
}
.fm-copy{
@include flcw(32upx, 44upx, $mColor);
}
.fm-admin{
@include ctf;
.fa-txt{
@include flcw(32upx, 44upx, #9A9A9D);
@include tHide;
.ft-txt{
font-size: 28upx;
}
}
.fa-copy{
padding: 0upx 10upx;
flex-shrink: 0;
@include flcw(28upx, 44upx, $mColor);
}
}
</style>

32
src/pages/menu/second.vue

@ -0,0 +1,32 @@
<template>
<view class="second-pages">
<view class="sp-post">
<view class="sp-box">
<view class="tl-tit">板块更新中</view>
</view>
</view>
<bottom-logo is-fixed></bottom-logo>
</view>
</template>
<script>
import bottomLogo from "@/subpackage/menu/components/bottom_logo.vue";
export default {
components:{ bottomLogo },
}
</script>
<style lang="scss">
.sp-post{
padding: 24upx;
.sp-box{
padding: 22upx 28upx;
height: 400upx;
background-image: linear-gradient(270deg, #d7d7da 0%, $mColor 100%);
.tl-tit{
@include flcw(43upx, 60upx, #fff, 500);
@include tHide;
}
}
}
</style>

117
src/pages/menu/third.vue

@ -0,0 +1,117 @@
<template>
<view class="third-page">
<view class="tp-container">
<image class="tc-post" mode="aspectFit" src="/static/images/third_pages/banner.png"></image>
<view class="tc-bot">
<view class="tb-left" @click="clickTap">
<image class="tl-bg" mode="aspectFill" src="/static/images/third_pages/bg.png"></image>
<view class="tl-tit">采购商城</view>
<view class="tl-txt">采购专业装备</view>
</view>
<view class="tb-right">
<view class="tr-item active" @click="clickTap">
<view class="ti-title">客服外包</view>
<view class="ti-text">客服外包</view>
<image class="tr-icon" src="/static/images/third_pages/tab_a.png"></image>
</view>
<view class="tr-item" @click="clickTap">
<view class="ti-title">敬请期待</view>
<view class="ti-text">敬请期待</view>
<image class="tr-icon" src="/static/images/third_pages/tab_b.png"></image>
</view>
<view class="tr-item" @click="clickTap">
<view class="ti-title">敬请期待</view>
<view class="ti-text">敬请期待</view>
<image class="tr-icon" src="/static/images/third_pages/tab_b.png"></image>
</view>
</view>
</view>
</view>
<bottom-logo is-fixed></bottom-logo>
</view>
</template>
<script>
import bottomLogo from "@/subpackage/menu/components/bottom_logo.vue";
import { showNone } from "@/utils/util";
export default {
components:{ bottomLogo },
methods: {
clickTap(){
showNone('板块更新中');
}
}
}
</script>
<style lang="scss">
.third-page{
.tp-container{
padding: 28upx 24upx 0upx;
.tc-post{
display: block;
height: 400upx;
width: 100%;
}
.tc-bot{
margin-top: 24upx;
display: flex;
align-items: stretch;
.tb-left{
position: relative;
flex-shrink: 0;
margin-right: 22upx;
padding: 22upx 28upx;
width: 340upx;
height: 420upx;
.tl-bg{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.tl-tit{
@include flcw(43upx, 60upx, #fff, 500);
@include tHide;
}
.tl-txt{
@include flcw(30upx, 42upx, #fff, 500);
@include tHide;
}
}
.tb-right{
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.tr-item{
position: relative;
height: 124upx;
background-image: linear-gradient(270deg, #FFFFFF 0%, #C8C8CA 100%);
padding: 16upx;
&.active{
background-image: linear-gradient(270deg, #F2D286 0%, #E0A46F 100%);
}
.ti-title{
@include flcw(32upx, 44upx, #fff, 500);
@include tHide;
}
.ti-text{
@include flcw(22upx, 32upx, #fff, 500);
@include tHide;
}
.tr-icon{
position: absolute;
right: 6upx;
bottom: 0upx;
width: 136upx;
height: 104upx;
}
}
}
}
}
}
</style>

4
src/pages/merchant_login/merchant_login.vue

@ -8,11 +8,11 @@
</view>
</template>
<script>
import util from '../../utils/util'
import util, { jsonStr } from '../../utils/util'
export default {
methods: {
toWebView(){
util.routeTo(`/pages/web_view/web_view?src=${encodeURIComponent("https://www.ouxuanzhineng.cn")}`,'rL');
util.routeTo(`/pages/web_view/web_view?src=${jsonStr("https://www.ouxuanzhineng.cn/")}`,'rL');
}
}
}

3
src/pages/web_view/web_view.vue

@ -2,6 +2,7 @@
<web-view :src="address"></web-view>
</template>
<script>
import { jsonPar } from "@/utils/util.js";
export default {
data(){
return {
@ -10,7 +11,7 @@ export default {
},
onLoad(options){
console.log('web_view options', options);
this.address = decodeURIComponent(options?.src || 'https://www.ouxuanzhineng.cn');
this.address = jsonPar(decodeURIComponent(options?.src ?? ''));
}
}
</script>

BIN
src/static/images/icon/arrow_b2.png

Before

Width: 40  |  Height: 40  |  Size: 251 B

After

Width: 15  |  Height: 15  |  Size: 226 B

BIN
src/static/images/icon/index/green_bg_circle.png

Before

Width: 296  |  Height: 296  |  Size: 51 KiB

After

Width: 296  |  Height: 296  |  Size: 7.7 KiB

BIN
src/static/images/icon/index/tab_12.png

Before

Width: 52  |  Height: 52  |  Size: 2.4 KiB

After

Width: 52  |  Height: 52  |  Size: 901 B

BIN
src/static/images/icon/index/tab_13.png

After

Width: 52  |  Height: 52  |  Size: 1.2 KiB

BIN
src/static/images/icon/index/tab_8.png

Before

Width: 52  |  Height: 52  |  Size: 2.4 KiB

After

Width: 52  |  Height: 52  |  Size: 775 B

BIN
src/static/images/icon/scan_code_btn.png

Before

Width: 172  |  Height: 172  |  Size: 932 B

BIN
src/static/images/icon/selected_ring.png

Before

Width: 32  |  Height: 32  |  Size: 419 B

BIN
src/static/images/icon/write_off_fail.png

Before

Width: 420  |  Height: 420  |  Size: 9.6 KiB

BIN
src/static/images/tab/ta_1.png

After

Width: 56  |  Height: 56  |  Size: 558 B

BIN
src/static/images/tab/ta_2.png

After

Width: 56  |  Height: 56  |  Size: 795 B

BIN
src/static/images/tab/ta_3.png

After

Width: 56  |  Height: 56  |  Size: 726 B

BIN
src/static/images/tab/ta_4.png

After

Width: 56  |  Height: 56  |  Size: 553 B

BIN
src/static/images/tab/tab_1.png

After

Width: 56  |  Height: 56  |  Size: 653 B

BIN
src/static/images/tab/tab_2.png

After

Width: 56  |  Height: 56  |  Size: 986 B

BIN
src/static/images/tab/tab_3.png

After

Width: 56  |  Height: 56  |  Size: 939 B

BIN
src/static/images/tab/tab_4.png

After

Width: 56  |  Height: 56  |  Size: 668 B

BIN
src/static/images/third_pages/banner.png

After

Width: 351  |  Height: 200  |  Size: 23 KiB

BIN
src/static/images/third_pages/bg.png

After

Width: 170  |  Height: 210  |  Size: 2.5 KiB

BIN
src/static/images/third_pages/tab_a.png

After

Width: 68  |  Height: 57  |  Size: 2.3 KiB

BIN
src/static/images/third_pages/tab_b.png

After

Width: 68  |  Height: 57  |  Size: 1.9 KiB

74
src/store/actions.js

@ -1,6 +1,8 @@
// 异步方法
import { servers } from '../js/server';
import { API } from '../js/api';
import { showLoad, hideLoad, showModal, promisify } from '../utils/util';
export default {
getBrandInfo({commit, state}){
if(state?.brandInfo?.brand?.id)return Promise.resolve(state.brandInfo);
@ -24,5 +26,75 @@ export default {
.catch(err=>{
console.warn('actions getOrderRefundList err -->', err);
})
}
},
// 获取门店列表
getStadiumLs({ commit, state }, brand_id){
showLoad();
return servers.post({
url: API.stadiumList,
data: { page_size: 9999, page: 1, brand_id },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
let _ls = _data?.data?.list || [];
return _ls;
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '加载店铺失败!'
})
console.warn('store actions getStadiumLs err --->', err);
// return Promise.reject(err);
})
},
// 看是否授权,清除token 也可以理解自动登陆
async checkUserAuthor({ commit, state }){
let loginRes = {};
try{
const uniLogin = promisify(uni.login);
loginRes = await uniLogin();
}catch(err){
console.warn('store actions checkUserAuthor login err', err);
showModal({ content: '获取登陆凭证失败!请稍后重新登录!' });
return Promise.reject(err);
}
showLoad();
return servers.post({
url: API.wechatMiniAppLoginAndSync,
data: {
code: loginRes.code,
appid: state.APPID,
},
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
if(_data.data === ''){
commit('setLoginState', { loginState: false, token: '' });
return _data;
}
commit('setLoginState', { loginState: true, token: _data.data });
return _data;
}else{
return Promise.reject(_data);
}
})
.catch(err=>{
console.warn('store actions checkUserAuthor wechatMiniAppLoginAndSync err --->', err);
hideLoad();
showModal({ content: err?.message || '静默登录失败!请稍后重试!' });
return Promise.reject(err);
})
},
}

24
src/store/index.js

@ -33,19 +33,20 @@ export default new Vuex.Store({
// },
permissionObj: { // 权限代号对应
'1001': '营业额',
'1002': '收款记录',
// '1002': '收款记录',
'1012': '订单管理',
'1007': '员工管理',
'1008': '核销查询',
'1009': '场地管理',
'1010': '设备管理',
'1009': '场地看板',
'1010': '智能设备',
'1011': '商品零售',
// '1013': '课程管理',
'1014': '储值卡管理',
'1015': '进场人数异常',
'1016': '系统工具',
'1017': '钱包提现',
'1018': '退款权限'
// '1015': '进场人数异常',
'1016': '小程序管理',
'1017': '钱包&提现',
'1018': '退款权限',
'1022': '工单'
},
// 场地占用提交页面信息
occupyInfo: {
@ -54,7 +55,8 @@ export default new Vuex.Store({
typeInfo: {}, // 球场类型
venueList: [], // 选择场地列表
orderInfo: {}, // 订单信息 // 20230620 新增挂账需求,用于回显确认订单
}
},
isLogin: false, // 登陆状态
},
mutations,
actions,
@ -69,6 +71,12 @@ export default new Vuex.Store({
}
console.warn(_arr.length)
return _arr;
},
loginState: state =>{
let _isLogin = state.isLogin;
let _token = uni.getStorageSync('token');
// 只是想获得一个响应的token状态
if(_isLogin || !_isLogin)return !!_token;
}
}
});

13
src/store/mutations.js

@ -10,5 +10,18 @@ export default {
// 场地占用信息
setOccupyInfo(state, _occupyInfo){
state.occupyInfo = _occupyInfo;
},
//
setLoginState(state, { loginState = false, token = '' }){
console.log(loginState, token);
if(loginState&&token){
uni.setStorageSync('token', token);
state.isLogin = loginState;
}else{
uni.removeStorageSync('token', token);
state.isLogin = false;
}
}
}

188
src/subpackage/authorization/components/login.vue

@ -0,0 +1,188 @@
<template>
<view class="authorization-login" v-show="isShow">
<view class="ic-author-modal">
<view class="iam-title">微信授权</view>
<view class="iam-tip">您的信息和数据将受到保护</view>
<image class="iam-pic" mode="aspectFit" src="../static/images/author_modal.png"></image>
<view class="iam-btns">
<button plain hover-class="hover-active" @click="hide">取消</button>
<button
v-if="isProfile"
plain
hover-class="hover-active"
@click="profileConfirm"
>授权并登录</button>
<button
v-else
plain
hover-class="hover-active"
open-type="getUserInfo"
lang="zh_CN"
@getuserinfo="confirmAuthor"
>授权并登录</button>
</view>
</view>
</view>
</template>
<script>
import { promisify, showModal, showNone, showLoad, hideLoad, routeTo } from "@/utils/util";
import { AUTHOR_API } from "../js/api";
import server from "../js/server";
export default {
data(){
return {
isShow: false,
initData: {
/**
* @param {Object} data
* @param {Function} data.success
* @param {Function} data.fail
* */
},
}
},
methods: {
alert(data){
this.isShow = true;
this.initData = data ?? {};
},
hide(){
this.isShow = false;
},
isProfile(){
return !!uni.getUserProfile
},
//
profileConfirm(){
uni.getUserProfile({
lang: 'zh_CN', desc: '授权登陆',
success: res => { this.confirmAuthor({detail: {...res}}) },
fail: err => {
showModal({ content: '获取用户信息失败!请重试' });
console.warn('subpackage authorization components login getUserProfile Err ->', err)
}
})
},
async confirmAuthor(userRes){
if(!userRes.detail.userInfo){
this.hide();
return showModal({ content: '获取用户信息失败!请稍后重试' });
}
let loginRes = await promisify(uni.login)();
if(!loginRes.code){
this.hide();
return showModal({ content: '获取登陆凭证失败!请稍后重试' });
}
this.serverLogin({ userRes, loginRes })
},
getLoginQuery({
userInfo,
loginRes
}){
return {
appid: uni.getAccountInfoSync?.()?.miniProgram?.appId ?? '',
code: loginRes?.code ?? '',
encryptedData: userInfo?.encryptedData ?? '',
// is_details: 1,
//
user_info: userInfo?.userInfo ?? {},
user_raw_data: userInfo?.rawData ?? {},
...(userInfo?.userInfo ?? {}),
}
},
serverLogin({ userRes, loginRes }){
showLoad();
return server.post({
url: AUTHOR_API.wechatMiniAppLoginAndSync,
data: this.getLoginQuery({
userInfo: userRes.detail,
loginRes
}),
isDefaultGet: false,
})
.then(res=>{
hideLoad();
let _data = res.data || {};
if(_data.code == 0){
if(_data.data == '')return routeTo(`/pages/login/login`,'rL');
showNone(_data?.message || '登陆成功!');
this.$store.commit('setLoginState', { loginState: true, token: _data.data });
this.initData?.success?.(_data);
this.hide();
return _data;
}else{
return Promise.reject(_data);
}
})
.catch(err=>{
hideLoad();
showModal({ content: err?.message || '后台登陆失败!' });
console.warn('subpackage authorization components login serverLogin Err ->', err);
if(this.initData?.fail)return this.initData?.fail?.(err);
return Promise.reject(err);
})
},
cancelAuthor(){
}
}
}
</script>
<style lang="scss">
.authorization-login{
position: fixed;
left: 0;
top: var(--window-top);
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .5);
}
.ic-author-modal{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding-top: 60upx;
width: 662upx;
height: 884upx;
border-radius: 10upx;
background-color: #fff;
.iam-title{
margin-bottom: 22upx;
text-align: center;
@include flcw(44upx, 60upx, #1a1a1a, 500);
}
.iam-tip{
margin-bottom: 52upx;
text-align: center;
@include flcw(28upx, 40upx, #9c9c9f);
}
.iam-pic{
margin: 0 auto 62upx;
display: block;
width: 488upx;
height: 416upx;
}
.iam-btns{
@include ctf(center);
>button{
margin: 0 20upx;
width: 240upx;
height: 92upx;
text-align: center;
border-radius: 46upx;
border: 2upx solid $mColor;
@include flcw(32upx, 88upx, $mColor);
&+button{
background-color: $mColor;
color: #fff;
}
}
}
}
</style>

25
src/subpackage/authorization/components/user_info/iconfont.css

@ -0,0 +1,25 @@
@charset "UTF-8";
@font-face {
font-family: "tuniaoFont"; /* Project id 3784643 */
src:
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAN8AAsAAAAAB4gAAAMvAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDBgqCQIIdATYCJAMMCwgABCAFhH8HPRuYBiMRpmvPIfurAzsYPj7DCVbrRAmElfXCs4V6xacO8aHO69VXu54tTy8h0hcJ7ufgieY+3+xscrn9B8wKQQFBywplLZCw9SwcOyZ5pKsoAQAbcICJTr7AjPPqr06NRkeIAemFpXv8cje1+/LkuLvD789WJZUANjOe9F76qptDOSMUui/Ua0WQKpZGXaqf5hHZdAD6x8tRJtRKtQd7j5UKkk6lWywYkGADCZBnGLugJd6CAI4GOjH956Ep4k7imdNkYTGFAkm2nJyPrBNwA7YhWwgQ/K4bLXITD8wl8xxuBN+XwyYJwOAp5jFjOwNbPNwd3l3WiyFAv3sCBLjcAB6gQM3GjLLSbJpDo1ANiqsiFiX7WAQzqTcMA06j/pb/eAbEx8yMAaAChg3sDjs6COwub8IAoAD4PEZkA7iAZClPKdicmJLc2OiZcQC3cV613sc3Ab9W7YItQtio9YgdKGjgr+ANlbg5gPvXFacv8IyvKTH4pcjV7VIcfnuIAUd3VCbxcCIKj++rvr/hpx/iwxd4/5mxDXeILbBp2+ByMIlGMuQEU3gjiVaMcXk8Dn2gWo5wuRwuIldLRqRwWogvmCRXE9/kSvrVQP4iFDVWRSbRakMk/AtcBYVpO6rpah1V9J/m/Wt+q8WfsOV+S5KFTGD5dwW9KFstZUs5smuYK3V495zwUG+r/Izf0PWtbWj/HUQsAL9u+cGK8NCiAgRnaRJ49fqljrxWc4PaKs4pQwBlMLUeAzgcEGLakvg5nCZ3zgTBkZzAEEUuFEeJyMrq4BGhGT6OPjiqGd4eIZkLKGKjgTKuAAgpPIAhjmdQUngnsrJdeKTxBz4pkgjHjJQeFqFMXv8PQqg0QP+F2cbAdoHqFmaM52jYlcbNoeTq4CtPkjzHAE1Zl0QvFEjGmp9fY6vKwBI9kOvIuQhJ4kJWy0k1dVXFVXcpbfTmIIRKA/RfmG0M7G1T3cKM8VwMXGlEnR1KY0uU+CTJnM1ODk1ZdxJdSOjYdGOp+TW2k6szsNg8qIvIuQipetFCVstpQCB1lcXHXUl5ZOpfdBvgMI9QMaJi8TZdzLS36K8/ueDZOWMAAA==') format('woff2'),
url('//at.alicdn.com/t/c/font_3784643_5jru9pe5fad.woff?t=1669045092678') format('woff'),
url('//at.alicdn.com/t/c/font_3784643_5jru9pe5fad.ttf?t=1669045092678') format('truetype');
}
[class*='tn-icon-'] {
font-family: 'tuniaoFont' !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
text-align: center;
text-decoration: none;
}
.tn-icon-close:before {
content: "\e74d";
}
.tn-icon-camera-fill:before {
content: "\e75d";
}

133
src/subpackage/authorization/components/user_info/impower.vue

@ -0,0 +1,133 @@
<template>
<wx-user-info-modal
v-model="showAuthorizationModal"
@updated="updatedUserInfoEvent"
:isModal="isModal"
:alway="alway"
></wx-user-info-modal>
</template>
<script>
import WxUserInfoModal from './tuniaoui-wx-user-info.vue';
import { AUTHOR_API } from '../../js/api';
import servers from '../../js/server';
import { showModal, jsonPar, showLoad, hideLoad } from '@/utils/util';
export default {
components: { WxUserInfoModal },
props: {
isModal: {
type: Boolean,
default: true
},
alway: {
type: Boolean,
default: false
}
},
watch: {
showAuthorizationModal(nVal, oVal){
let { initData } = this;
if(!nVal&&oVal)initData?.close?.();
}
},
data() {
return {
showAuthorizationModal: false,
initData: {},
}
},
methods: {
show(data){
this.showAuthorizationModal = true;
this.initData = data;
},
hide(){
this.showAuthorizationModal = false;
},
//
async updatedUserInfoEvent(info) {
let { initData, userEdit } = this;
try{
let _imgUrl = await this.uploadImg(info.avatar);
let _editObj = { nickname: info.nickname, avatar_url: _imgUrl };
await userEdit(_editObj);
this.hide();
initData?.success?.(_editObj);
}catch(err){
console.warn('authorize components user info impower updatedUserInfoEvent err --->', err)
initData?.fail?.(err);
}
},
//
async uploadImg(url){
try{
showLoad();
let _imgInfo = await servers.uploadFile({
url: AUTHOR_API.zs_user_avatar,
filePath: url,
});
if(_imgInfo.statusCode != 200)throw(_imgInfo);
let _imgRes = jsonPar(_imgInfo.data);
if(_imgRes.code != 0)throw(_imgRes);
hideLoad();
return _imgRes.data.url || '';
}catch(err){
hideLoad();
showModal({ content: err?.errMsg ?? err?.message ?? '上传图片失败!' });
console.warn('authorize components user info impower uploadImg err --->', err)
return Promise.reject(err);
}
},
//
userEdit({ nickname, avatar_url }){
showLoad();
return servers.post({
url: AUTHOR_API.changeAvatar,
data: { nickname, avatar_url },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
console.log('userEdit res--->', _data);
return _data?.data;
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({ content: err.message || '操作失败!' });
console.warn('authorize components user info impower userEdit err --->', err);
return Promise.reject(err);
})
},
//
refreshUserInfo(){
return servers.post({
url: ATR_API.userCurrent,
data: {},
isDefaultGet: false,
})
.then(res => {
let _data = res?.data || {};
if(_data.code === 0){
uni.setStorageSync('userInfo', _data?.data);
this.$store.commit('refreshUserInfo', _data?.data);
return _data?.data;
}else{
return Promise.reject(_data);
}
})
.catch(err => {
console.warn('authorize components user info impower refreshUserInfo err --->', err);
// return Promise.reject(err);
})
}
}
}
</script>
<style lang="scss">
</style>

357
src/subpackage/authorization/components/user_info/tuniaoui-wx-user-info.vue

@ -0,0 +1,357 @@
<template>
<view
v-if="openModal || alway"
class="wx-authorization-modal"
:class="{ 'modal-class': isModal }"
>
<view
class="wam__mask"
@touchmove.prevent=""
@tap.stop="closeModal"
></view>
<!-- 内容区域 -->
<view class="wam__wrapper">
<!-- 关闭按钮 -->
<view class="wam__close-btn" @tap.stop="closeModal">
<text class="tn-icon-close"></text>
</view>
<!-- 标题 -->
<view class="wam__title">获取您的昵称头像</view>
<!-- tips -->
<view class="wam__sub-title">
获取用户头像昵称主要用于向用户提供具有辨识度的用户中心界面
</view>
<!-- 头像选择 -->
<view class="wam__avatar">
<view class="button-shadow">
<button
class="button"
open-type="chooseAvatar"
@chooseavatar="chooseAvatarEvent"
>
<view v-if="userInfo.avatar" class="avatar__image">
<image class="image" :src="userInfo.avatar" mode="aspectFill"></image>
</view>
<view v-else class="avatar__empty">
<!-- <image class="image" src="https://cdn.nlark.com/yuque/0/2022/jpeg/280373/1668928062708-assets/web-upload/764843cf-055a-4cb6-b5d3-dca528b33fd4.jpeg" mode="aspectFill"></image> -->
</view>
<view class="avatar--icon">
<view class="tn-icon-camera-fill"></view>
</view>
</button>
</view>
</view>
<!-- 昵称输入 -->
<view class="wam__nickname">
<view class="nickname__data">
<input class="input" type="nickname" v-model="userInfo.nickname" placeholder="请输入昵称" placeholder-style="color: #AAAAAA;">
</view>
</view>
<!-- 保存按钮 -->
<view
class="wam__submit-btn"
:class="[{
'disabled': !userInfo.avatar || !userInfo.nickname
}]"
hover-class="tn-btn-hover-class"
:hover-stay-time="150"
@tap.stop="submitUserInfo"
>
</view>
</view>
</view>
</template>
<script>
export default {
options: {
// Vue(shadow)
virtualHost: true
},
props: {
value: {
type: Boolean,
default: false
},
isModal: {
type: Boolean,
default: true
},
alway: {
type: Boolean,
default: false
}
},
data() {
return {
openModal: false,
userInfo: {
avatar: '',
nickname: ''
}
}
},
watch: {
value: {
handler(val) {
this.openModal = val
},
immediate: true
}
},
methods: {
//
chooseAvatarEvent(e) {
this.userInfo.avatar = e.detail.avatarUrl
},
//
submitUserInfo() {
//
if (!this.userInfo.avatar || !this.userInfo.nickname) {
return uni.showToast({
icon: 'none',
title: '请选择头像和输入用户信息'
})
}
//
this.$emit('updated', this.userInfo)
},
//
closeModal() {
this.$emit('input', false)
},
}
}
</script>
<style lang="scss" scoped>
@import './iconfont.css';
.modal-class{
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
z-index: 99998;
.wam {
&__mask {
display: block !important;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
opacity: 0;
animation: showMask 0.25s ease 0.1s forwards;
}
&__close-btn {
display: block !important;
display: none;
position: absolute;
top: 30rpx;
right: 30rpx;
z-index: 99999;
}
&__wrapper{
position: absolute;
left: 0;
bottom: 0;
width: 100%;
transform-origin: center bottom;
transform: scaleY(0);
animation: showWrapper 0.25s ease 0.1s forwards;
z-index: 99999;
}
}
}
.wx-authorization-modal {
// view {
// box-sizing: border-box;
// }
.image {
width: 100%;
height: 100%;
border-radius: inherit;
}
.wam {
/* mask */
&__mask {
display: none;
}
/* close-btn */
&__close-btn {
display: none;
}
/* wrapper */
&__wrapper {
background-color: #FFFFFF;
border-radius: 20rpx 20rpx 0rpx 0rpx;
padding: 40rpx;
padding-top: 60rpx;
padding-bottom: 40rpx;
padding-bottom: calc(constant(safe-area-inset-bottom) + 40rpx);
padding-bottom: calc(env(safe-area-inset-bottom) + 40rpx);
}
/* title */
&__title {
font-size: 34rpx;
}
/* sub-title */
&__sub-title {
font-size: 26rpx;
color: #AAAAAA;
margin-top: 16rpx;
padding-bottom: 30rpx;
}
/* 头像选择 */
&__avatar {
width: 100%;
margin-top: 30rpx;
display: flex;
align-items: center;
justify-content: center;
.button-shadow {
border: 8rpx solid rgba(255,255,255,0.05);
box-shadow: 0rpx 0rpx 80rpx 0rpx rgba(0, 0, 0, 0.15);
border-radius: 50%;
}
.button {
position: relative;
width: 160rpx;
height: 160rpx;
border-radius: 50%;
overflow: visible;
background-image: repeating-linear-gradient(45deg, #E4E9EC, #F8F7F8);
color: #FFFFFF;
background-color: transparent;
padding: 0;
margin: 0;
font-size: inherit;
line-height: inherit;
border: none;
&::after {
border: none;
}
}
.avatar {
&__empty, &__image {
width: 100%;
height: 100%;
border-radius: inherit;
}
&--icon {
position: absolute;
right: -10rpx;
bottom: -6rpx;
width: 60rpx;
height: 60rpx;
// transform: translate(50%, 50%);
background-color: #1D2541;
color: #FFFFFF;
border-radius: 50%;
border: 6rpx solid #FFFFFF;
line-height: 1;
font-size: 36rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
}
/* 昵称 */
&__nickname {
margin-top: 40rpx;
.nickname {
&__data {
margin-top: 16rpx;
width: 100%;
padding: 26rpx 20rpx;
border-radius: 10rpx;
background-color: #F8F7F8;
.input {
color: #080808;
}
}
}
}
/* 保存按钮 */
&__submit-btn {
width: 100%;
background-color: $mColor;
color: #FFFFFF;
margin-top: 60rpx;
border-radius: 10rpx;
padding: 25rpx;
font-size: 32rpx;
display: flex;
align-items: center;
justify-content: center;
&.disabled {
background-color: #E6E6E6;
}
}
}
}
.tn-btn-hover-class {
box-shadow: inset 10rpx 2rpx 40rpx 0rpx rgba(0, 0, 0, 0.05);
}
@keyframes showMask {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes showWrapper {
0% {
transform: scaleY(0);
}
100% {
transform: scaleY(1);
}
}
</style>

9
src/subpackage/authorization/js/api.js

@ -0,0 +1,9 @@
import { ORIGIN } from '@/js/api';
export const AUTHOR_API = {
wechatMiniAppLoginAndSync: `${ORIGIN}/assistant/WechatMiniAppGetToken`, // 小程序授权获取token,为空就登录
zs_user_avatar:`${ORIGIN}/upload/file/zs_user_avatar`, // 头像图片上传
changeAvatar: `${ORIGIN}/admin/assistant/changeAvatar`, // 修改用户头像、昵称
}
export default AUTHOR_API;

10
src/subpackage/authorization/js/server.js

@ -0,0 +1,10 @@
import { Server } from '@/js/server';
class _Server extends Server {
constructor(props){
super(props)
}
}
export default new _Server();

16
src/subpackage/authorization/pages/index.vue

@ -0,0 +1,16 @@
<template>
<!-- 需要定义一个页面以及在pagesjson里声明才能生成static文件夹 -->
<view>&nbsp;</view>
</template>
<script>
export default {
}
</script>
<style lang="scss">
page{
background: #fff;
}
</style>

0
src/static/images/icon/author_modal.png → src/subpackage/authorization/static/images/author_modal.png

Before

Width: 488  |  Height: 416  |  Size: 14 KiB

After

Width: 488  |  Height: 416  |  Size: 14 KiB

41
src/subpackage/menu/components/bottom_logo.vue

@ -0,0 +1,41 @@
<template>
<view class="bottom-logo" :class="{ 'fixed-active': isFixed }" @click="toWebView">
<image @click="toWebView" class="bl-img" mode="aspectFit" src="/subpackage/menu/static/images/bot_logo.png"></image>
</view>
</template>
<script>
import { routeTo, jsonStr } from '@/utils/util.js'
export default {
props: {
isFixed: {
type: Boolean,
default: false
}
},
methods: {
toWebView(){
routeTo(`/pages/web_view/web_view?src=${jsonStr('https://www.ouxuanzhineng.cn/')}`, 'nT');
}
}
}
</script>
<style lang="scss">
.bottom-logo{
height: 200upx;
width: 100%;
@include ctf(center);
&.fixed-active{
position: fixed;
left: 50%;
bottom: 0;
transform: translateX(-50%);
z-index: -1;
}
}
.bl-img{
width: 312upx;
height: 100upx;
}
</style>

87
src/subpackage/menu/components/mine/header.vue

@ -0,0 +1,87 @@
<template>
<view class="mine-header">
<view class="mh-user">
<image class="mu-photo" mode="aspectFill" :src="photo"></image>
<view class="mu-info" v-if="isLogin">
<view class="mi-line">昵称{{ nickname || '-' }}</view>
<view class="mi-line">姓名{{ name || '-' }}</view>
<view class="mi-line">账号{{ account || '-' }}</view>
</view>
<button v-else class="mu-login" @click="$emit('click:login')">点击登录</button>
</view>
<view class="mh-update" v-if="isLogin" @click="$emit('click:update')">更新头像昵称</view>
</view>
</template>
<script>
export default {
props: {
isLogin: {
type: Boolean,
default: false
},
nickname: {
type: String,
default: ''
},
name: {
type: String,
default: ''
},
account: {
type: String,
default: ''
},
photo: {
type: String,
default: ''
}
},
mounted(){
console.log('mounted');
this.$emit('on:munted');
},
}
</script>
<style lang="scss">
.mine-header{
padding-top: 70upx;
height: 270upx;
background: $mColor;
.mh-user{
padding-left: 68upx;
padding-right: 30upx;
@include ctf;
.mu-photo{
flex-shrink: 0;
margin-right: 28upx;
width: 120upx;
height: 120upx;
border-radius: 50%;
border: 4upx solid #FFFFFF;
}
.mu-info{
.mi-line{
@include flcw(32upx, 44upx, #FFFFFF);
@include tHide;
}
}
.mu-login{
@include clearBtn;
width: 180upx;
height: 72upx;
text-align: center;
border-radius: 6upx;
background: #fff;
@include flcw(32upx, 72upx, $mColor, 500);
}
}
.mh-update{
padding-left: 68upx;
margin-top: 8upx;
text-decoration: underline;
@include flcw(20upx, 28upx, #FFFFFF);
}
}
</style>

63
src/subpackage/menu/components/mine/line_tab.vue

@ -0,0 +1,63 @@
<template>
<view class="line-tab" @click="$emit('click')">
<image class="lt-icon" mode="aspectFit" :src="'/subpackage/menu/static/images/mine_tab/' + iconNum + '.png'"></image>
<view class="lt-right">
<view class="lr-left">
<view class="lr-name">
<slot name="default">账号管理</slot>
</view>
</view>
<view class="lr-right">
<slot name="right">
<image class="lr-arrow" mode="aspectFit" src="/subpackage/menu/static/images/arrow_b2.png"></image>
</slot>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
iconNum: {
type: Number,
default: 0
}
}
}
</script>
<style lang="scss">
.line-tab{
padding-left: 22upx;
padding-right: 44upx;
height: 108upx;
background: #fff;
@include ctf;
.lt-icon{
flex-shrink: 0;
margin-right: 22upx;
width: 48upx;
height: 48upx;
}
.lt-right{
flex-grow: 1;
@include ctf(space-between);
.lr-left{
.lr-name{
@include flcw(32upx, 48upx, #9A9A9D);
@include tHide;
}
}
.lr-right{
flex-shrink: 0;
max-width: 60%;
font-size: 0;
.lr-arrow{
width: 30upx;
height: 30upx;
}
}
}
}
</style>

16
src/subpackage/menu/pages/index.vue

@ -0,0 +1,16 @@
<template>
<!-- 需要定义一个页面以及在pagesjson里声明才能生成static文件夹 -->
<view class="menu-index">&nbsp;</view>
</template>
<script>
export default {
}
</script>
<style lang="scss">
page{
background: #fff;
}
</style>

BIN
src/subpackage/menu/static/images/arrow_b2.png

After

Width: 15  |  Height: 15  |  Size: 226 B

BIN
src/subpackage/menu/static/images/bot_logo.png

After

Width: 312  |  Height: 100  |  Size: 3.4 KiB

BIN
src/subpackage/menu/static/images/mine_tab/0.png

After

Width: 96  |  Height: 96  |  Size: 1.3 KiB

BIN
src/subpackage/menu/static/images/mine_tab/1.png

After

Width: 96  |  Height: 96  |  Size: 1.5 KiB

BIN
src/subpackage/menu/static/images/mine_tab/2.png

After

Width: 96  |  Height: 96  |  Size: 1.0 KiB

BIN
src/subpackage/menu/static/images/mine_tab/3.png

After

Width: 96  |  Height: 96  |  Size: 1.4 KiB

BIN
src/subpackage/menu/static/images/mine_tab/4.png

After

Width: 96  |  Height: 96  |  Size: 1.0 KiB

BIN
src/subpackage/menu/static/images/mine_tab/5.png

After

Width: 96  |  Height: 96  |  Size: 942 B

BIN
src/subpackage/menu/static/images/mine_tab/6.png

After

Width: 96  |  Height: 96  |  Size: 640 B

59
src/subpackage/message/components/detail/answer_item.vue

@ -0,0 +1,59 @@
<template>
<view class="answer-item" :class="{ 'active-bg': activeBg }">
<view class="ai-user">
<view class="au-name">{{ name || '-' }}</view>
<view class="au-time">[回复] {{ time || '-' }}</view>
</view>
<view class="ai-content">{{ content || '-' }}</view>
</view>
</template>
<script>
export default {
props: {
name: {
type: String,
default: ''
},
time: {
type: String,
default: ''
},
content: {
type: String,
default: ''
},
activeBg: {
type: Boolean,
default: false
}
}
}
</script>
<style lang="scss">
.answer-item{
padding: 30upx 22upx;
background: #fff;
border-radius: 10upx;
&.active-bg{
background: #faeada;
}
}
.ai-user{
@include ctf;
@include flcw(32upx, 44upx, #1A1A1A);
.au-name{
@include tHide;
}
.au-time{
flex-shrink: 0;
color: #9C9C9F;
}
}
.ai-content{
padding-left: 26upx;
margin-top: 24upx;
@include flcw(28upx, 40upx, #9C9C9F);
}
</style>

51
src/subpackage/message/components/detail/image_flow.vue

@ -0,0 +1,51 @@
<template>
<view class="image-flow">
<image
class="if-img"
v-for="(e, i) in imgs"
:key="i"
:src="e"
mode="aspectFill"
@click="previewImg(e)"
></image>
</view>
</template>
<script>
export default {
props: {
imgs: {
type: Array,
default: ()=>[]
}
},
methods: {
previewImg(url) {
let { imgs } = this;
uni.previewImage({
urls: imgs,
current: url
})
}
}
}
</script>
<style lang="scss">
.image-flow{
padding: 30upx 14upx;
background: #fff;
border-radius: 20upx;
font-size: 0;
.if-img{
margin: 0 12upx;
width: 200upx;
height: 200upx;
border-radius: 10upx;
&:nth-of-type(n+4){
margin-top: 28upx;
}
}
}
</style>

60
src/subpackage/message/components/detail/info.vue

@ -0,0 +1,60 @@
<template>
<view class="detail-info">
<view class="di-line">
<view class="dl-name">标题</view>
<view class="dl-value">{{ title || '-' }}</view>
</view>
<view class="di-line">
<view class="dl-name">类型</view>
<view class="dl-value">{{ type || '-' }}</view>
</view>
<view class="di-line">
<view class="dl-name">反馈内容</view>
<view class="dl-value">{{ content || '-' }}</view>
</view>
</view>
</template>
<script>
export default {
props: {
title: {
type: String,
default: ''
},
type: {
type: String,
default: ''
},
content: {
type: String,
default: ''
}
},
}
</script>
<style lang="scss">
.detail-info{
padding: 30upx 24upx;
border-radius: 10upx;
background: #fff;
.di-line{
display: flex;
align-items: flex-start;
justify-content: space-between;
&+.di-line{
margin-top: 10upx;
}
.dl-name{
flex-shrink: 0;
@include flcw(28upx, 40upx, #1a1a1a);
}
.dl-value{
flex-grow: 1;
text-align: right;
@include flcw(28upx, 40upx, #1a1a1a);
}
}
}
</style>

37
src/subpackage/message/components/edit/fixed_button.vue

@ -0,0 +1,37 @@
<template>
<view class="fixed-button">
<button class="fb-btn" @click="$emit('click:cancel')">取消</button>
<button class="fb-btn" @click="$emit('click:submit')">提交</button>
</view>
</template>
<script>
export default {
}
</script>
<style lang="scss">
.fixed-button{
position: fixed;
left: 0;
bottom: 0;
padding: 10upx 20upx;
width: 100%;
@include isPd(10upx);
@include ctf(center);
.fb-btn{
margin: 0 20upx;
width: 240upx;
text-align: center;
border: 2upx solid $mColor;
border-radius: 44upx;
background: #fff;
@include flcw(32upx, 88upx, $mColor);
&+.fb-btn{
background: $mColor;
color: #fff;
}
}
}
</style>

195
src/subpackage/message/components/edit/info_edit.vue

@ -0,0 +1,195 @@
<template>
<view class="info-edit">
<!-- 门店picker -->
<view class="ie-stadium" v-if="isStadium">
<view class="ie-title"><text class="it-star">*</text>门店</view>
<picker mode="selector" :range="stadiumList" range-key="name" @change="stadiumChange">
<view class="is-box">
<input type="text" class="ib-ipt" placeholder="请选择门店" :value="fromData.curStadium.name || ''" disabled/>
<image class="ib-icon" mode="aspectFit" src="/subpackage/message/static/images/unfold.png"></image>
</view>
</picker>
</view>
<!-- 标题 -->
<view class="ie-section">
<view class="ie-title"><text class="it-star">*</text>标题</view>
<input type="text" class="ie-ipt" placeholder="请输入" v-model="fromData.titleStr" />
</view>
<!-- 类型 -->
<view class="ie-types" v-if="isStadium">
<view class="ie-title"><text class="it-star">*</text>类型</view>
<view class="it-ls">
<view class="il-item" v-for="(e, i) in ['硬件', '软件']" :key="i" @click="fromData.typeStr = e">
<view class="ii-icon" :class="{ 'active': e === fromData.typeStr }"></view>
<view class="ii-txt">{{ e || '' }}</view>
</view>
</view>
</view>
<!-- 反馈内容 -->
<view class="ie-section">
<view class="ie-title"><text class="it-star">*</text>反馈内容</view>
<textarea class="ie-tarea" placeholder="请输入" v-model="fromData.feedbackStr"></textarea>
</view>
</view>
</template>
<script>
import { showNone } from "@/utils/util"
export default {
props: {
isStadium: {
type: Boolean,
default: false
},
isType: {
type: Boolean,
default: false
}
},
data(){
return {
stadiumList: [],
fromData: {
curStadium: {},
titleStr: '',
typeStr: '硬件',
feedbackStr: '',
},
}
},
methods: {
async initStadiumLs({ brand_id, stadium_id }){
let _stadiums = await this.$store?.dispatch?.('getStadiumLs', brand_id);
if(_stadiums.length > 0)this.stadiumList = _stadiums ?? [];
if(stadium_id){
let _curStadium = _stadiums.find(item => item.id === stadium_id);
if(_curStadium?.id)this.fromData.curStadium = _curStadium;
}
},
stadiumChange(e){
let { value } = e.detail;
if(value === -1)return;
this.fromData.curStadium = this.stadiumList[value];
},
async getData(){
let { fromData, isStadium, isType } = this;
let { curStadium, titleStr, typeStr, feedbackStr } = fromData;
if(titleStr === ''){
showNone('请输入标题');
return Promise.reject('请输入标题');
}
if(feedbackStr === ''){
showNone('请输入反馈内容');
return Promise.reject('请输入反馈内容');
}
if(isStadium&&!curStadium?.id){
showNone('请选择门店');
return Promise.reject('请选择门店');
}
let _data = {
title: titleStr,
content: feedbackStr,
}
if(isType)_data['type'] = typeStr;
if(isStadium)_data['stadium_id'] = curStadium.id;
return _data;
}
}
}
</script>
<style lang="scss">
.info-edit{
padding: 30upx;
border-radius: 20upx;
background: #fff;
.ie-title{
flex-shrink: 0;
@include flcw(32upx, 44upx, #1A1A1A, 500);
.it-star{
color: #EA5061;
}
}
.ie-section{
margin-top: 24upx;
}
.ie-ipt, .ie-tarea{
display: block;
margin-top: 24upx;
padding: 26upx 20upx;
border-radius: 16upx;
background: #F6F8F9;
@include flcw(28upx, 40upx, #1A1A1A);
}
.ie-tarea{
height: 306upx;
width: auto;
}
.ie-stadium{
@include ctf;
.ie-title{
margin-right: 30upx;
}
.is-box{
padding: 0 22upx;
width: 478upx;
height: 92upx;
border: 2upx solid #979797;
border-radius: 16upx;
@include ctf;
.ib-ipt{
height: 100%;
flex-grow: 1;
@include flcw(28upx, 40upx, #1A1A1A);
}
.ib-icon{
margin-left: 20upx;
flex-shrink: 0;
width: 28upx;
height: 28upx;
}
}
}
.ie-types{
margin-top: 24upx;
@include ctf;
.ie-title{
margin-right: 76upx;
}
.it-ls{
@include ctf;
.il-item{
width: 200upx;
@include ctf;
.ii-icon{
position: relative;
width: 40upx;
height: 40upx;
border-radius: 50%;
border: 2upx solid #D8D8D8;
&.active{
border-color: $mColor;
&::before{
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 28upx;
height: 28upx;
border-radius: 50%;
background: $mColor;
}
}
}
.ii-txt{
margin-left: 12upx;
@include flcw(28upx, 40upx, #1A1A1A);
}
}
}
}
}
</style>

189
src/subpackage/message/components/edit/upload_img.vue

@ -0,0 +1,189 @@
<template>
<view class="upload-img">
<view class="ui-title">上传照片<text class="ut-txt">(最多上传{{ max }}只支持.jpgpng 格式)</text></view>
<view class="ui-ls">
<view class="ul-item" v-for="(e, i) in imgTemLs" :key="i">
<image class="ui-pic" @click="previewImg(e)" mode="aspectFill" :src="e"></image>
<image class="ui-close" @click="delImg(i)" mode="aspectFit" src="/subpackage/message/static/images/close.png"></image>
</view>
<view class="ul-item ul-add" @click="chooseImg" v-if="max > imgTemLs.length">
<view class="ua-icon"></view>
<view class="ua-txt">上传照片</view>
</view>
</view>
</view>
</template>
<script>
import { showModal, jsonPar, showLoad, hideLoad, promisify } from "@/utils/util";
import server from "../../js/server";
import { MESSAGE_API } from "../../js/api";
export default {
props: {
max: {
type: Number,
default: 6
}
},
data(){
return {
imgTemLs: [],
}
},
methods: {
chooseImg(){
let { max } = this;
uni.chooseImage({
count: max,
sourceType: ['album', 'camera'],
success: (res) => {
let { imgTemLs, max } = this;
let _curImgNum = imgTemLs?.length ?? 0;
let _temImgNum = res?.tempFilePaths?.length ?? 0;
if((_curImgNum + _temImgNum) > max)return showModal({ content: `最多上传${max}张图片!` });
this.imgTemLs = imgTemLs.concat(res.tempFilePaths);
}
});
},
delImg(idx){
this.imgTemLs.splice(idx, 1);
},
previewImg(url){
uni.previewImage({ urls: this.imgTemLs, current: url });
},
async getUrls(){
try{
let _urls = [];
let { imgTemLs } = this;
showLoad('图片上传中...');
for(let i = 0; i < imgTemLs?.length; i++){
let _url = await this.uploadImg(imgTemLs[i]);
if(_url !== '')_urls.push(_url);
}
if(_urls?.length < imgTemLs?.length){
let _showModal = promisify(showModal);
hideLoad();
if(_urls?.length === 0){
return _showModal({ content: '图片上传失败,请稍后重试' })
.then(_=>Promise.reject('全部图片上传失败'));
}
let _diffVal = imgTemLs?.length - _urls?.length;
return _showModal({
content: `${_diffVal}张图片上传失败`,
showCancel: true,
cancelText: '重试',
confirmText: '继续提交',
})
.then(res=>{
if(res.confirm)return { temps: imgTemLs, urls: _urls };
return Promise.reject(res);
})
}
hideLoad();
return {
temps: imgTemLs,
urls: _urls,
};
}catch(err){
hideLoad();
console.warn('message components edit upload_img getUrls err --->', err)
}
},
async uploadImg(url){
try{
let _imgInfo = await server.uploadFile({
url: MESSAGE_API.zs_message_imgs,
filePath: url,
});
if(_imgInfo.statusCode != 200)throw(_imgInfo);
let _imgRes = jsonPar(_imgInfo.data);
if(_imgRes.code != 0)throw(_imgRes);
return _imgRes?.data?.url ?? '';
}catch(err){
// showModal({
// titile: '',
// content: err?.errMsg ?? err?.message ?? ''
// });
console.warn('message components edit upload_img uploadImg err --->', err)
// return Promise.reject(err);
return '';
}
},
}
}
</script>
<style lang="scss">
.upload-img{
padding: 30upx 18upx;
border-radius: 20upx;
background: #fff;
.ui-title{
padding: 0 10upx;
@include flcw(32upx, 44upx, #333333);
.ut-txt{
font-size: 24upx;
color: #9A9A9D;
}
}
.ui-ls{
margin-top: 24upx;
font-size: 0;
.ul-item{
position: relative;
margin: 0 10upx;
vertical-align: top;
display: inline-block;
width: 200upx;
height: 200upx;
&:nth-of-type(n+4){
margin-top: 24upx;
}
.ui-pic{
width: 100%;
height: 100%;
border-radius: 10upx;
}
.ui-close{
position: absolute;
top: 6upx;
right: 6upx;
width: 40upx;
height: 40upx;
}
}
.ul-add{
padding-top: 36upx;
border: 2upx solid #D8D8D8;
.ua-icon{
position: relative;
margin: 0 auto;
width: 60upx;
height: 60upx;
&::before, &::after{
content: '';
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: block;
width: 100%;
height: 4upx;
background: #D8D8D8;
}
&::after{
transform: translate(-50%, -50%) rotate(90deg);
}
}
.ua-txt{
text-align: center;
margin-top: 28upx;
@include flcw(28upx, 40upx, #9A9A9D);
}
}
}
}
</style>

33
src/subpackage/message/components/fixed_button.vue

@ -0,0 +1,33 @@
<template>
<view class="fixed-button">
<button class="fb-btn" hover-class="hover-active" @click="$emit('click')">新建</button>
</view>
</template>
<script>
export default {
}
</script>
<style lang="scss">
.fixed-button{
position: fixed;
left: 0;
bottom: 0;
width: 100%;
padding: 10upx 24upx;
@include isPd(10upx);
.fb-btn{
@include clearBtn;
text-align: center;
height: 112upx;
background: $mColor;
border-radius: 10upx;
@include flcw(32upx, 112upx, #fff, 500);
}
.hover-active{
opacity: .9;
}
}
</style>

85
src/subpackage/message/components/message_item.vue

@ -0,0 +1,85 @@
<template>
<view class="message-item" @click="$emit('click:item')">
<image class="mi-icon" mode="aspectFit" src="/subpackage/message/static/images/message.png"></image>
<view class="mi-content">
<view class="mc-info">
<view class="mi-top">
<view class="mt-title">{{ title || '-' }}</view>
<view class="mt-tag" v-if="status">{{ status || '-' }}</view>
</view>
<view class="mi-text">{{ content || '-' }}</view>
</view>
<image class="mc-arrow" mode="aspectFit" src="/subpackage/message/static/images/arrow_b2.png"></image>
</view>
</view>
</template>
<script>
export default {
props: {
title: {
type: String,
default: ''
},
status: {
type: String,
default: ''
},
content: {
type: String,
default: ''
}
},
}
</script>
<style lang="scss">
.message-item{
padding: 30upx 24upx;
border-radius: 10upx;
background: #fff;
@include ctf;
.mi-icon{
flex-shrink: 0;
margin-right: 24upx;
width: 108upx;
height: 108upx;
}
.mi-content{
flex-grow: 1;
@include ctf;
.mc-info{
flex-grow: 1;
.mi-top{
@include ctf;
.mt-title{
@include flcw(32upx, 44upx, #1A1A1A);
@include tHide;
}
.mt-tag{
flex-shrink: 0;
margin-left: 20upx;
padding: 4upx 12upx;
border: 2upx solid $mColor;
border-radius: 6upx;
text-align: center;
@include flcw(24upx, 34upx, $mColor);
}
}
.mi-text{
margin-top: 18upx;
@include flcw(28upx, 40upx, #9C9C9F);
@include tHide(2);
}
}
.mc-arrow{
align-self: flex-start;
flex-shrink: 0;
margin-left: 20upx;
margin-top: 8upx;
width: 28upx;
height: 28upx;
}
}
}
</style>

16
src/subpackage/message/js/api.js

@ -0,0 +1,16 @@
import { ORIGIN } from '@/js/api';
export const MESSAGE_API = {
zs_message_imgs:`${ORIGIN}/upload/file/zs_message_imgs`, // 投诉/工单图片
complaintSubmit:`${ORIGIN}/admin/assistant/complaint/submit`, // 投诉提交
complaintList:`${ORIGIN}/admin/assistant/complaint/list`, // 投诉列表
complaintInfo:`${ORIGIN}/admin/assistant/complaint/info`, // 投诉详情
complaintReply:`${ORIGIN}/admin/assistant/complaint/reply`, // 投诉回复
workorderSubmit:`${ORIGIN}/admin/assistant/workorder/submit`, // 工单提交
workorderList:`${ORIGIN}/admin/assistant/workorder/list`, // 工单列表
workorderInfo:`${ORIGIN}/admin/assistant/workorder/info`, // 工单详情
workorderReply:`${ORIGIN}/admin/assistant/workorder/reply`, // 工单回复
}
export default { ORIGIN, MESSAGE_API };

9
src/subpackage/message/js/server.js

@ -0,0 +1,9 @@
import { Server } from '@/js/server';
class _server extends Server {
constructor(props){
super(props)
}
}
export default new _server();

171
src/subpackage/message/pages/complaint/detail.vue

@ -0,0 +1,171 @@
<template>
<view class="complaint-detail">
<view class="top-status">当前状态{{ orderInfo.status || '-' }}</view>
<view class="heigh-space" style="height: 24upx;"></view>
<detail-info
:title="orderInfo.title || '-'"
:type="orderInfo.type || '-'"
:content="orderInfo.content || '-'"
></detail-info>
<block v-if="imgArr.length">
<view class="heigh-space" style="height: 24upx;"></view>
<image-flow :imgs="imgArr"></image-flow>
</block>
<view class="heigh-space" style="height: 24upx;"></view>
<button class="right-btn" @click="replyBtn">回复</button>
<block v-for="(e, i) in commentArr" :key="i">
<view class="heigh-space" style="height: 24upx;"></view>
<answer-item
:name="e.user_name || ''"
:time="e.reply_time || ''"
:content="e.content || ''"
:active-bg="e.is_creator"
></answer-item>
</block>
</view>
</template>
<script>
import detailInfo from "../../components/detail/info.vue";
import imageFlow from "../../components/detail/image_flow.vue";
import answerItem from "../../components/detail/answer_item.vue";
import { routeTo, debounce, showLoad, hideLoad, showModal, showNone } from "@/utils/util.js";
import { MESSAGE_API } from "../../js/api.js";
import server from "../../js/server.js";
export default {
components: {
detailInfo, imageFlow, answerItem
},
computed: {
imgArr(){
let { orderInfo } = this;
let _showimgs = orderInfo?.showimgs ?? [];
return _showimgs;
},
commentArr(){
let { orderInfo } = this;
let _comment = orderInfo?.comment ?? [];
return _comment;
}
},
data(){
return {
orderInfo: {}
}
},
onLoad(options){
this.complaintInfo({
brand_id: options?.brand_id ?? '',
id: options?.id ?? ''
})
},
methods: {
replyBtn(){
let { orderInfo } = this;
showModal({
title: '回复',
showCancel: true,
editable: true,
placeholderText: '请输入回复内容',
success: mRes=>{
console.log('replyBtn mRes --->', mRes);
if(mRes?.confirm && mRes?.content)this.complaintReply({
brand_id: orderInfo?.brand_id ?? '',
id: orderInfo?.id ?? '',
reply_text: mRes?.content ?? ''
})
.then(res =>{
if(res === 'success'){
setTimeout(_=>{
this.complaintInfo({
brand_id: orderInfo?.brand_id ?? '',
id: orderInfo?.id ?? ''
})
}, 1000);
}
})
}
})
},
complaintInfo({ brand_id, id }){
showLoad();
return server.post({
url: MESSAGE_API.complaintInfo,
data: { brand_id: +brand_id, id: +id },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
let _info = _data?.data ?? {};
return this.orderInfo = _info;
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '加载失败!'
})
console.warn('subpackage message pages complaint detail complaintInfo err --->', err);
// return Promise.reject(err);
})
},
complaintReply({ brand_id, id, reply_text }){
showLoad();
return server.post({
url: MESSAGE_API.complaintReply,
data: { brand_id: +brand_id, id: +id, reply_text },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
showNone(_data?.message ?? '回复成功!');
return 'success'
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '操作失败!'
})
console.warn('subpackage message pages complaint detail complaintReply err --->', err);
// return Promise.reject(err);
})
},
},
}
</script>
<style lang="scss">
.complaint-detail{
padding: 24upx;
@include isPd(24upx);
}
.top-status{
padding: 0 24upx;
text-align: center;
border-radius: 10upx;
background: #fff;
@include tHide;
@include flcw(28upx, 84upx, #1A1A1A);
}
.right-btn{
margin-left: auto;
margin-right: 0upx;
width: 192upx;
text-align: center;
border-radius: 10upx;
background: $mColor;
@include flcw(32upx, 88upx, #FFFFFF);
}
</style>

87
src/subpackage/message/pages/complaint/edit.vue

@ -0,0 +1,87 @@
<template>
<view class="complaint-edit">
<info-edit ref="infoEdit"></info-edit>
<view style="height: 24rpx;"></view>
<upload-img ref="uploadImg"></upload-img>
<fixed-button
@click:cancel="cancelBtn"
@click:submit="submitBtn"
></fixed-button>
</view>
</template>
<script>
import infoEdit from "../../components/edit/info_edit.vue";
import uploadImg from "../../components/edit/upload_img.vue";
import fixedButton from "../../components/edit/fixed_button.vue";
import { routeTo, debounce, showLoad, hideLoad, showModal, showNone } from "@/utils/util.js";
import { MESSAGE_API } from "../../js/api.js";
import server from "../../js/server.js";
export default {
components: { infoEdit, uploadImg, fixedButton },
data() {
return {
brand_id: '',
}
},
onLoad(options) {
let _bid = options?.brand_id ?? '';
this.brand_id = _bid;
},
methods: {
cancelBtn: routeTo,
submitBtn: debounce(async function(){
try{
let { brand_id } = this;
let _infoData = await this.$refs?.infoEdit?.getData?.();
let { urls, temps } = await this.$refs?.uploadImg?.getUrls?.();
this.complaintSubmit({
brand_id,
title: _infoData?.title ?? '',
content: _infoData?.content ?? '',
showimgs: urls ?? [],
})
}catch(err){
hideLoad();
console.warn('subpackage message pages complaint edit submitBtn err --->', err);
}
}, 300, true),
//
complaintSubmit({ brand_id, title = '', content = '', showimgs = [] }){
showLoad();
return server.post({
url: MESSAGE_API.complaintSubmit,
data: { brand_id: +brand_id, title, content, showimgs },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
showNone(_data?.message ?? '操作成功!');
setTimeout(routeTo, 1000);
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '操作失败!'
})
console.warn('subpackage message pages complaint edit complaintSubmit err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.complaint-edit{
padding: 24upx;
@include isPd(120upx);
}
</style>

105
src/subpackage/message/pages/complaint/list.vue

@ -0,0 +1,105 @@
<template>
<view class="complaint-list">
<view class="cl-ls">
<block v-for="(e, i) in listData" :key="i">
<message-item
:title="e.title"
:status="e.status"
:content="e.content"
@click:item="itemClick(e)"
></message-item>
<view style="height: 24rpx;"></view>
</block>
</view>
<fixed-button @click="clickBtn">新建投诉</fixed-button>
</view>
</template>
<script>
import fixedButton from "../../components/fixed_button.vue";
import messageItem from "../../components/message_item.vue";
import { routeTo, debounce, showLoad, hideLoad, showModal, showNone } from "@/utils/util.js";
import { MESSAGE_API } from "../../js/api.js";
import server from "../../js/server.js";
export default {
components: {
fixedButton,
messageItem,
},
data(){
return {
brand_id: '',
listData: [],
page: 1
}
},
onLoad(options){
let _bid = options?.brand_id ?? '';
this.brand_id = _bid;
this.complaintList({ brand_id: _bid });
},
async onPullDownRefresh(){
let { brand_id } = this;
this.listData = [];
this.page = 1;
await this.complaintList({ brand_id });
uni.stopPullDownRefresh();
},
onReachBottom(){
let { page, brand_id } = this;
this.complaintList({ page: page + 1, brand_id });
},
methods: {
itemClick(e){
let { brand_id } = this;
let _qryStr = `brand_id=${brand_id ?? ''}&id=${e?.id ?? ''}`;
routeTo(`/subpackage/message/pages/complaint/detail?${_qryStr}`, 'nT');
},
clickBtn(){
let { brand_id } = this;
routeTo(`/subpackage/message/pages/complaint/edit?brand_id=${brand_id ?? ''}`, 'nT');
},
complaintList({ brand_id, page = 1, page_size = 20}){
showLoad();
return server.post({
url: MESSAGE_API.complaintList,
data: { brand_id: +brand_id, page, page_size },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
let _ls = _data?.data ?? [];
if(page === 1)return this.listData = _ls;
if(!_ls.length)return showNone('没有更多!');
this.page = page;
this.listData = [ ...this.listData, ..._ls ];
return _ls;
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '加载失败!'
})
console.warn('subpackage message pages complaint list complaintList err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.complaint-list{
@include isPd(132upx);
.cl-ls{
padding: 28upx 24upx 0;
}
}
</style>

171
src/subpackage/message/pages/work_order/detail.vue

@ -0,0 +1,171 @@
<template>
<view class="complaint-detail">
<view class="top-status">当前状态{{ orderInfo.status || '-' }}</view>
<view class="heigh-space" style="height: 24upx;"></view>
<detail-info
:title="orderInfo.title || '-'"
:type="orderInfo.type || '-'"
:content="orderInfo.content || '-'"
></detail-info>
<block v-if="imgArr.length">
<view class="heigh-space" style="height: 24upx;"></view>
<image-flow :imgs="imgArr"></image-flow>
</block>
<view class="heigh-space" style="height: 24upx;"></view>
<button class="right-btn" @click="replyBtn">回复</button>
<block v-for="(e, i) in commentArr" :key="i">
<view class="heigh-space" style="height: 24upx;"></view>
<answer-item
:name="e.user_name || ''"
:time="e.reply_time || ''"
:content="e.content || ''"
:active-bg="e.is_creator"
></answer-item>
</block>
</view>
</template>
<script>
import detailInfo from "../../components/detail/info.vue";
import imageFlow from "../../components/detail/image_flow.vue";
import answerItem from "../../components/detail/answer_item.vue";
import { routeTo, debounce, showLoad, hideLoad, showModal, showNone } from "@/utils/util.js";
import { MESSAGE_API } from "../../js/api.js";
import server from "../../js/server.js";
export default {
components: {
detailInfo, imageFlow, answerItem
},
computed: {
imgArr(){
let { orderInfo } = this;
let _showimgs = orderInfo?.showimgs ?? [];
return _showimgs;
},
commentArr(){
let { orderInfo } = this;
let _comment = orderInfo?.comment ?? [];
return _comment;
}
},
data(){
return {
orderInfo: {}
}
},
onLoad(options){
this.workorderInfo({
brand_id: options?.brand_id ?? '',
id: options?.id ?? ''
})
},
methods: {
replyBtn(){
let { orderInfo } = this;
showModal({
title: '回复',
showCancel: true,
editable: true,
placeholderText: '请输入回复内容',
success: mRes=>{
console.log('replyBtn mRes --->', mRes);
if(mRes?.confirm && mRes?.content)this.workorderReply({
brand_id: orderInfo?.brand_id ?? '',
id: orderInfo?.id ?? '',
reply_text: mRes?.content ?? ''
})
.then(res =>{
if(res === 'success'){
setTimeout(_=>{
this.workorderInfo({
brand_id: orderInfo?.brand_id ?? '',
id: orderInfo?.id ?? ''
})
}, 1000);
}
})
}
})
},
workorderInfo({ brand_id, id }){
showLoad();
return server.post({
url: MESSAGE_API.workorderInfo,
data: { brand_id: +brand_id, id: +id },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
let _info = _data?.data ?? {};
return this.orderInfo = _info;
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '加载失败!'
})
console.warn('subpackage message pages work_order detail workorderInfo err --->', err);
// return Promise.reject(err);
})
},
workorderReply({ brand_id, id, reply_text }){
showLoad();
return server.post({
url: MESSAGE_API.workorderReply,
data: { brand_id: +brand_id, id: +id, reply_text },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
showNone(_data?.message ?? '回复成功!');
return 'success'
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '操作失败!'
})
console.warn('subpackage message pages work_order detail workorderReply err --->', err);
// return Promise.reject(err);
})
},
},
}
</script>
<style lang="scss">
.complaint-detail{
padding: 24upx;
@include isPd(24upx);
}
.top-status{
padding: 0 24upx;
text-align: center;
border-radius: 10upx;
background: #fff;
@include tHide;
@include flcw(28upx, 84upx, #1A1A1A);
}
.right-btn{
margin-left: auto;
margin-right: 0upx;
width: 192upx;
text-align: center;
border-radius: 10upx;
background: $mColor;
@include flcw(32upx, 88upx, #FFFFFF);
}
</style>

99
src/subpackage/message/pages/work_order/edit.vue

@ -0,0 +1,99 @@
<template>
<view class="work-order-edit">
<info-edit
ref="infoEdit"
is-stadium
is-type
></info-edit>
<view style="height: 24rpx;"></view>
<upload-img ref="uploadImg"></upload-img>
<fixed-button
@click:cancel="cancelBtn"
@click:submit="submitBtn"
></fixed-button>
</view>
</template>
<script>
import infoEdit from "../../components/edit/info_edit.vue";
import uploadImg from "../../components/edit/upload_img.vue";
import fixedButton from "../../components/edit/fixed_button.vue";
import { routeTo, debounce, showLoad, hideLoad, showModal, showNone } from "@/utils/util.js";
import { MESSAGE_API } from "../../js/api.js";
import server from "../../js/server.js";
export default {
components: { infoEdit, uploadImg, fixedButton },
data() {
return {
brand_id: '',
}
},
onLoad(options) {
let _bid = options?.brand_id ?? '';
this.brand_id = _bid;
this.$nextTick(_=>{
this.$refs?.infoEdit?.initStadiumLs?.({
brand_id: _bid,
stadium_id: +(options?.stadium_id ?? '')
});
})
},
methods: {
cancelBtn: routeTo,
submitBtn: debounce(async function(){
try{
let { brand_id } = this;
let _infoData = await this.$refs?.infoEdit?.getData?.();
let { urls, temps } = await this.$refs?.uploadImg?.getUrls?.();
this.workorderSubmit({
brand_id,
stadium_id: _infoData?.stadium_id ?? '',
type: _infoData?.type ?? '',
title: _infoData?.title ?? '',
content: _infoData?.content ?? '',
showimgs: urls ?? [],
})
}catch(err){
hideLoad();
console.warn('subpackage message pages work order edit submitBtn err --->', err);
}
}, 300, true),
//
workorderSubmit({ brand_id, stadium_id, type, title = '', content = '', showimgs = [] }){
showLoad();
return server.post({
url: MESSAGE_API.workorderSubmit,
data: { brand_id: +brand_id, stadium_id, type, title, content, showimgs },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
showNone(_data?.message ?? '操作成功!');
setTimeout(routeTo, 1000);
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '操作失败!'
})
console.warn('subpackage message pages work order edit workorderSubmit err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.work-order-edit{
padding: 24upx;
@include isPd(120upx);
}
</style>

105
src/subpackage/message/pages/work_order/list.vue

@ -0,0 +1,105 @@
<template>
<view class="complaint-list">
<view class="cl-ls">
<block v-for="(e, i) in listData" :key="i">
<message-item
:title="e.title"
:status="e.status"
:content="e.content"
@click:item="itemClick(e)"
></message-item>
<view style="height: 24rpx;"></view>
</block>
</view>
<fixed-button @click="clickBtn">新建工单</fixed-button>
</view>
</template>
<script>
import fixedButton from "../../components/fixed_button.vue";
import messageItem from "../../components/message_item.vue";
import { routeTo, debounce, showLoad, hideLoad, showModal, showNone } from "@/utils/util.js";
import { MESSAGE_API } from "../../js/api.js";
import server from "../../js/server.js";
export default {
components: {
fixedButton,
messageItem,
},
data(){
return {
brand_id: '',
listData: [],
page: 1
}
},
onLoad(options){
let _bid = options?.brand_id ?? '';
this.brand_id = _bid;
this.workorderList({ brand_id: _bid });
},
async onPullDownRefresh(){
let { brand_id } = this;
this.listData = [];
this.page = 1;
await this.workorderList({ brand_id });
uni.stopPullDownRefresh();
},
onReachBottom(){
let { page, brand_id } = this;
this.workorderList({ page: page + 1, brand_id });
},
methods: {
itemClick(e){
let { brand_id } = this;
let _qryStr = `brand_id=${brand_id ?? ''}&id=${e?.id ?? ''}`;
routeTo(`/subpackage/message/pages/work_order/detail?${_qryStr}`, 'nT');
},
clickBtn(){
let { brand_id } = this;
routeTo(`/subpackage/message/pages/work_order/edit?brand_id=${brand_id ?? ''}`, 'nT');
},
workorderList({ brand_id, page = 1, page_size = 20}){
showLoad();
return server.post({
url: MESSAGE_API.workorderList,
data: { brand_id: +brand_id, page, page_size },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
let _ls = _data?.data ?? [];
if(page === 1)return this.listData = _ls;
if(!_ls.length)return showNone('没有更多!');
this.page = page;
this.listData = [ ...this.listData, ..._ls ];
return _ls;
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '加载失败!'
})
console.warn('subpackage message pages work_order list workorderList err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.complaint-list{
@include isPd(132upx);
.cl-ls{
padding: 28upx 24upx 0;
}
}
</style>

BIN
src/subpackage/message/static/images/arrow_b2.png

After

Width: 15  |  Height: 15  |  Size: 226 B

BIN
src/subpackage/message/static/images/close.png

After

Width: 40  |  Height: 40  |  Size: 401 B

BIN
src/subpackage/message/static/images/message.png

After

Width: 54  |  Height: 54  |  Size: 959 B

BIN
src/subpackage/message/static/images/unfold.png

After

Width: 28  |  Height: 28  |  Size: 288 B

14
src/subpackage/verification/pages/record.vue

@ -16,7 +16,7 @@
<view class="vt-txt">{{ showPeriodStr }}</view>
<image class="vt-icon" mode="aspectFit" src="/subpackage/verification/static/images/calendar.png"></image>
</view>
<view class="vtb-num">核销数量{{ recordLs.length || 0 }}</view>
<view class="vtb-num">核销数量{{ verificationNum || 0 }}</view>
</view>
<view class="vr-list">
@ -109,6 +109,7 @@ export default {
curStadium: {},
recordLs: [],
page: 1,
verificationNum: 0,
}
},
/**
@ -123,6 +124,9 @@ export default {
//
let _selected = _stadiumLs.find(ele => +ele.id === +(options?.stadium_id ?? ''));
if(_selected?.id)this.curStadium = _selected;
} else if(_stadiumLs?.length){
// 便
this.curStadium = _stadiumLs[0];
}
this.reloadRecordLs();
},
@ -158,12 +162,12 @@ export default {
})
}, 300, true),
//
initPeriodStr(){
initPeriodStr(dayNum = 0){
let _today = new Date();
let _todayTimestamp = _today.getTime();
let _eStr = formatDate({ date: _today });
let _first30DaysTimestamp = _todayTimestamp - 30 * 24 * 60 * 60 * 1000;
let _sStr = formatDate({ date: new Date(_first30DaysTimestamp) });
let _firstDaysTimestamp = _todayTimestamp - dayNum * 24 * 60 * 60 * 1000;
let _sStr = formatDate({ date: new Date(_firstDaysTimestamp) });
return this.periodStr = `${_sStr}_${_eStr}`;
},
showPeriodModal(){
@ -243,6 +247,8 @@ export default {
let _data = res?.data || {};
if(_data.code === 0){
let _ls = this.formatRecordLs(_data?.data?.list || []);
let _total = _data?.data?.total ?? 0;
this.verificationNum = _total;
if(page === 1)return this.recordLs = _ls;
if(!_ls.length)return showNone('没有更多!');
this.page = page;

4
src/subpackage/wallet/pages/index/index.vue

@ -34,7 +34,7 @@ import walletModalSuccess from '../../components/wallet_modal_success.vue';
import rechargeModal from "./modules/recharge_modal.vue";
import { WALLET_API } from '../../js/api';
import servers from '../../js/server';
import { routeTo, showLoad, hideLoad, debounce, showModal } from '@/utils/util';
import { routeTo, showLoad, hideLoad, debounce, showModal, jsonStr } from '@/utils/util';
export default {
components: {
'wallet-info': walletInfo,
@ -80,7 +80,7 @@ export default {
})
}, 300, true),
toWebView(){
routeTo(`/pages/web_view/web_view?src=${encodeURIComponent(this.getAdminAddress())}`,'nT');
routeTo(`/pages/web_view/web_view?src=${jsonStr(this.getAdminAddress())}`,'nT');
},
getAdminAddress(){
let _appid = uni.getAccountInfoSync()?.miniProgram?.appId || '';

12
src/uni.scss

@ -105,4 +105,16 @@ $mColor: #009874;
padding-bottom: $height;
padding-bottom: calc( $height + constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */
padding-bottom: calc( $height + env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */
}
@mixin clearBtn{
margin: 0;
padding: 0;
line-height: 0;
background-color: transparent;
border-radius: 0;
&::after{
position: unset !important;
border: unset;
}
}

8
src/utils/util.js

@ -80,7 +80,9 @@ export function showModal({
confirmColor='#009874',
success,
fail,
complete
complete,
editable = false,
placeholderText = ''
}){
uni.showModal({
title,
@ -91,7 +93,9 @@ export function showModal({
confirmText,
success,
fail,
complete
complete,
editable,
placeholderText,
})
}

Loading…
Cancel
Save