初始上传

This commit is contained in:
2026-04-04 17:27:12 +08:00
parent 4d80d28eb4
commit b7e11774ee
11191 changed files with 1588469 additions and 0 deletions

4
addon/cashier/source/os/.gitignore vendored Executable file
View File

@@ -0,0 +1,4 @@
/unpackage
/.hbuilderx
/node_modules
/electron/node_modules

255
addon/cashier/source/os/App.vue Executable file
View File

@@ -0,0 +1,255 @@
<script>
import {checkPageAuth,getOrderRemind,pushBind,getPushStatus} from '@/api/config.js';
import {mapGetters} from 'vuex';
import config from 'common/js/config.js';
export default {
onLaunch: function (option) {
// #ifdef APP-PLUS
uni.getSystemInfo({
success: res => {
let fontsize = ((res.windowWidth * 16) / 1200) * 5.5 + 'px';
this.$store.commit('app/setRootSize', fontsize);
}
});
// #endif
if (uni.getStorageSync('globalStoreInfo')) {
this.$store.commit('app/setGlobalStoreInfo', uni.getStorageSync('globalStoreInfo'));
}
if (uni.getStorageSync('globalStoreId')) {
this.$store.commit('app/setGlobalStoreId', uni.getStorageSync('globalStoreId'));
}
if (uni.getStorageSync('defaultImg')) {
this.$store.commit('app/setDefaultImg', uni.getStorageSync('defaultImg'));
}
if (uni.getStorageSync('addon')) {
this.$store.commit('app/setAddon', uni.getStorageSync('addon'));
}
if (uni.getStorageSync('themeConfig')) {
this.$store.commit('app/setThemeConfig', uni.getStorageSync('themeConfig'));
}
this.$store.dispatch('app/getAddonIsExistFn');
this.$store.dispatch('app/getDefaultImgFn');
this.$store.dispatch('app/getThemeConfigFn');
this.$store.dispatch('app/getMemberSearchWayConfigFn');
if (uni.getStorageSync('cashierToken')) {
this.$store.dispatch('app/getStoreInfoFn');
this.$store.dispatch('app/getUserInfoFn');
this.$store.dispatch('app/getUserGroupFn');
}
this.$store.commit('app/setCurrRoute', '/' + option.path);
// #ifdef APP-PLUS
plus.webview.open(this.$config.baseUrl + '/cashier/pages/index/loading', 'loading');
uni.switchTab({
url: '/pages/reserve/index',
success: () => {
uni.switchTab({
url: '/pages/recharge/index',
success: () => {
uni.switchTab({
url: '/pages/verify/index',
success: () => {
if (!uni.getStorageSync('cashierToken')) {
uni.navigateTo({url: '/pages/login/login'});
} else {
uni.switchTab({url: '/pages/billing/index'});
}
plus.webview.close('loading');
}
});
}
});
}
});
// #endif
this.getOrderRemindFn()
this.getPushStatusFn()
this.initSocket()
},
onShow: function () {
if (!uni.getStorageSync('cashierToken')) {
// #ifdef H5
if (location.href.indexOf('pages/index/loading') == -1) {
this.$util.redirectTo('/pages/login/login', {}, 'redirectTo');
}
// #endif
// #ifndef H5
this.$util.redirectTo('/pages/login/login', {}, 'redirectTo');
// #endif
}
// this.$nextTick(()=>{
// window.addEventListener('beforeunload', this.handleBeforeUnload);
// })
},
methods: {
// handleBeforeUnload(e){
// // 提示用户确认
// e.preventDefault();
// },
getOrderRemindFn(){
getOrderRemind().then(res=>{
if(res.code>=0){
this.$store.dispatch('app/initOverallAudio',res.data);
}
})
},
getPushStatusFn(){
getPushStatus().then(res=>{
if(res.code>=0){
this.$store.dispatch('app/setIsSocketConnect',true)
}
})
},
initSocket(){
// 心跳机制
// 可自定义成你的模式,这里只做基本处理
let linkNumber = 1
let socketInterval=setInterval(()=>{
let token = uni.getStorageSync('cashierToken');
try{
if(token&&this.isSocketConnect){
if(linkNumber>3){
this.$util.showToast({
title: 'webSocket连接异常请联系管理员'
});
if(this.overallAudioIsPlay) this.$store.dispatch('app/setOverallAudioIsPlay',false);
this.$store.dispatch('app/setIsSocketConnect',false)
linkNumber = 1
return false
}
uni.sendSocketMessage({
data: 'ping',
success: (res) => {
linkNumber = 1
},
fail: (e) => {
linkNumber++
uni.connectSocket({
url:config.webSocket,
complete: (res) => {
},
fail: (connectE) => {
// console.log(e)
}
})
}
});
}
}catch(e){
//TODO handle the exception
}
},1000)
uni.onSocketMessage((res)=>{
var data = JSON.parse(res.data);
let token = uni.getStorageSync('cashierToken');
switch(data.type){
case 'init':
if(token){
this.$store.dispatch('app/setOverallAudioBindClientId',data.data.client_id)
pushBind({client_id:data.data.client_id})
}
break;
case 'ping':
uni.sendSocketMessage({
data: 'ping',
});
break;
default:
this.$store.dispatch('app/overallAudioPlay',data.data.audio);
}
});
},
initRoute(route) {
const search = function (menu, route, arr = []) {
menu.find((item, index) => {
if (item.path == route) {
arr.push(index);
return true;
} else if (item.children) {
arr = search(item.children, route, arr);
if (arr.length) {
arr.push(index);
return true;
} else {
return false;
}
}
return false;
});
return arr;
};
let menuIndex = search(this.menu, route).reverse();
this.$store.commit('app/setMenuIndex', {level: 'firstMenuIndex', index: menuIndex[0]});
this.$store.commit('app/setMenuIndex', {level: 'secondMenuIndex', index: menuIndex[1] ?? -1});
this.$store.commit('app/setMenuIndex', {level: 'thirdMenuIndex', index: menuIndex[2] ?? -1});
},
/**
* 检测页面是否有权限
*/
checkPageAuthFn() {
checkPageAuth(this.currRoute).then(res => {
if (res.code && res.code == -10012) {
this.$util.redirectTo('/pages/index/no_permission', {}, 'redirectTo');
}
});
}
},
computed: {
menu() {
let menu = require('@/common/menu/store.js');
return menu.default ?? [];
},
isSocketConnect(){
return this.$store.state.app.isSocketConnect
},
overallAudioIsPlay(){
return this.$store.state.app.overallAudioIsPlay
},
...mapGetters(['currRoute'])
},
watch: {
currRoute: function (nVal, oVal) {
if (nVal) {
this.initRoute(nVal);
this.checkPageAuthFn();
}
},
menu: function (nVal) {
if (nVal.length) {
this.initRoute(this.currRoute);
}
}
}
};
</script>
<style lang="scss">
/*每个页面公共css */
@import url('/common/css/iconfont.css');
@import '/common/css/common.scss';
@import '/common/css/form.scss';
uni-toast .uni-toast__content {
font-size: 0.16rem !important;
}
</style>

View File

@@ -0,0 +1,24 @@
import request from '@/common/js/http'
/**
* 获取地址数据
* @param {Object} params
*/
export function getAreaList(params) {
return request.post('/cashier/storeapi/address/arealist', {data: params})
}
/**
* 通过经纬度查询地址信息(地址名称)
*/
export function getTranAddressInfo(position) {
return request.post('/cashier/storeapi/address/tranaddressinfo', {data: {latlng: position}})
}
/**
* 通过地址名称查询详细地址信息(经纬度)
* @param {Object} name
*/
export function getAddressByName(name) {
return request.post('/cashier/storeapi/address/getaddressbyname', {data: {address: name}})
}

View File

@@ -0,0 +1,9 @@
import request from '@/common/js/http'
/**
* 获取卡项列表(分页)
* @param {Object} params
*/
export function getCardList(params) {
return request.post('/cashier/storeapi/card/page', {data: params})
}

View File

@@ -0,0 +1,121 @@
import request from '@/common/js/http'
/************************************整体设置+系统权限相关*******************************/
/**
* 获取收款设置
*/
export function getCollectMoneyConfig() {
return request.post('/cashier/storeapi/cashier/getcashiercollectmoneyconfig')
}
/**
* 收款设置
* @param {Object} params
*/
export function setCollectMoneyConfig(params) {
return request.post('/cashier/storeapi/cashier/setcashiercollectmoneyconfig', {data: params})
}
/**
* 检测页面是否有权限
* @param {Object} page
*/
export function checkPageAuth(page) {
return request.post('/cashier/storeapi/store/checkpageauth', {data: {page}})
}
/**
* 检测是否有新版本
* @param {Object} params
*/
export function checkUpdate(params) {
return request.post('/cashier/storeapi/appversion/checkupdate', {data: params})
}
/**
* 获取插件是否存在
* @param {Object} params
*/
export function getAddonIsExist(params) {
return request.post('/storeapi/addon/addonisexit')
}
/**
* 获取默认图
* @param {Object} params
*/
export function getDefaultImg(params) {
return request.post('/cashier/storeapi/cashier/defaultimg')
}
/**
* 设置收银台主题风格配置
* @param {Object} params
*/
export function setThemeConfig(params) {
return request.post('/cashier/storeapi/cashier/setThemeConfig', {data: params})
}
/**
* 获取收银台主题风格配置
*/
export function getThemeConfig() {
return request.post('/cashier/storeapi/config/getThemeConfig')
}
/**
* 获取收银台主题风格列表
*/
export function getThemeList() {
return request.post('/cashier/storeapi/cashier/getThemeList')
}
/**
* 设置收银台会员搜索方式配置
* @param {Object} params
*/
export function setMemberSearchWayConfig(params) {
return request.post('/cashier/storeapi/config/setMemberSearchWayConfig', {data: params})
}
/**
* 获取收银台会员搜索方式配置
*/
export function getMemberSearchWayConfig() {
return request.post('/cashier/storeapi/config/getMemberSearchWayConfig')
}
/**
* 获取收银台订单消息推送-配置
*/
export function getOrderRemind() {
return request.post('/cashier/storeapi/config/orderRemind')
}
/**
* 收银台消息推送-绑定门店
*/
export function pushBind(params) {
return request.post('/cashier/storeapi/push/bind',{data:params})
}
/**
* 收银台消息推送-更换绑定门店
*/
export function pushChangeBind(params) {
return request.post('/cashier/storeapi/push/changebind',{data:params})
}
/**
* 收银台消息推送-下线
*/
export function pushOffline() {
return request.post('/cashier/storeapi/push/offline',{data:params})
}
/**
* 获取收银台订单消息推送-服务状态
*/
export function getPushStatus() {
return request.post('/cashier/storeapi/push/status')
}

View File

@@ -0,0 +1,41 @@
import request from '@/common/js/http'
/**
* 获取配送员列表
* @param {Object} params
*/
export function getDeliverList(params) {
return request.post('/cashier/storeapi/store/deliverlists', {data: params})
}
/**
* 获取配送员详情
* @param {Object} deliver_id
*/
export function getDeliverInfo(deliver_id) {
return request.post('/cashier/storeapi/store/deliverinfo', {data: {deliver_id}})
}
/**
* 添加配送员
* @param {Object} params
*/
export function addDeliver(params) {
return request.post('/cashier/storeapi/store/adddeliver', {data: params})
}
/**
* 编辑配送员
* @param {Object} params
*/
export function editDeliver(params) {
return request.post('/cashier/storeapi/store/editdeliver', {data: params})
}
/**
* 删除配送员
* @param {Object} deliver_id
*/
export function deleteDeliver(deliver_id) {
return request.post('/cashier/storeapi/store/deletedeliver', {data: {deliver_id}})
}

View File

@@ -0,0 +1,124 @@
import request from '@/common/js/http'
/**
* 商品列表
* @param {Object} params
*/
export function getGoodsList(params) {
return request.post('/cashier/storeapi/goods/page', {data: params})
}
/**
* 商品详情
* @param {Object} goods_id
*/
export function getGoodsDetail(goods_id) {
return request.post('/cashier/storeapi/goods/detail', {data: {goods_id}})
}
/**
* 编辑商品
* @param {Object} params
*/
export function editGoods(params) {
return request.post('/cashier/storeapi/goods/editgoods', {data: params})
}
/**
* 编辑商品
* @param {Object} params
*/
export function setGoodsLocalRestrictions(params) {
return request.post('/cashier/storeapi/goods/setGoodsLocalRestrictions', {data: params})
}
/**
* 通过扫码事件查询会员信息
* @param {Object} sku_no
*/
export function getGoodsInfoByCode(sku_no) {
return request.post('/cashier/storeapi/goods/skuinfo', {data: {sku_no}})
}
/**
* 设置商品状态
* @param {Object} params
*{ goods_id: arr.toString(), status: status}
*/
export function setGoodsStatus(params) {
return request.post('/cashier/storeapi/goods/setstatus', {data: params})
}
/**
* 商品分类
* @param {Object} params
*/
export function getGoodsCategory(params) {
return request.post('/cashier/storeapi/goods/category', {data: params})
}
/**
* 商品分类[库存]
* @param {Object} params
*/
export function getManageGoodsCategory(params = {}) {
return request.post('/stock/storeapi/manage/getGoodsCategory', {data: params})
}
/**
* 项目分类
* @param {Object} params
*/
export function getServiceCategory(params) {
return request.post('/cashier/storeapi/service/category', {data: params})
}
/**
* 项目列表
* @param {Object} params
*/
export function getServiceList(params) {
return request.post('/cashier/storeapi/service/page', {data: params})
}
/**
* 获取商品项目多规格规格数据
* @param {Object} goods_id
*/
export function getGoodsSkuList(goods_id) {
return request.post('/cashier/storeapi/goods/skulist', {data: {goods_id}})
}
/**
* 获取电子秤信息
*/
export function getElectronicScaleInformation() {
return request.post('/scale/storeapi/scale/cashierscale')
}
/**
* 商品搜索条件
* @param {Object} params
*/
export function getGoodsSceen(params = {}) {
return request.post('/cashier/storeapi/goods/screen', {data: params})
}
/**
* 规格选择
* @param {Object} params
*/
export function getSkuListBySelect(params = {}) {
return request.post('/cashier/storeapi/goods/getSkuListBySelect', {data: params})
}
/**
* 导出打印价格标签数据
* @param {Object} params
*/
export function exportPrintPriceTagData(params = {}) {
return request.post('/cashier/storeapi/goods/exportPrintPriceTagData', {data: params})
}

View File

@@ -0,0 +1,26 @@
import request from '@/common/js/http'
/**
* 获取验证码
* @param captcha_id
* @returns {*}
*/
export function getCaptcha(captcha_id) {
return request.post('/storeapi/captcha/captcha', {data: {captcha_id}})
}
/**
* 登录
* @param {Object} params
*/
export function login(params) {
return request.post('/cashier/storeapi/login/login', {data: params})
}
/**
* 修改密码
* @param {Object} params
*/
export function modifyPassword(params) {
return request.post('/cashier/storeapi/user/modifypassword', {data: params})
}

View File

@@ -0,0 +1,48 @@
import request from '@/common/js/http'
/************************************优惠券*******************************/
/**
* 优惠券详情
* @param {Object} coupon_type_id
*/
export function getCouponDetail(coupon_type_id) {
return request.post('/coupon/storeapi/coupon/detail', {data: {coupon_type_id}})
}
/**
* 优惠券详情领取记录
* @param {Object} coupon_type_id
*/
export function getReceiveCouponPageList(coupon_type_id) {
return request.post('/coupon/storeapi/membercoupon/getReceiveCouponPageList', {data: {coupon_type_id}})
}
/**
* 新增优惠券
*/
export function addCoupon(params) {
return request.post('/coupon/storeapi/coupon/add', {data: params})
}
/**
* 编辑优惠券
*/
export function editCoupon(params) {
return request.post('/coupon/storeapi/coupon/edit', {data: params})
}
/**
* 关闭优惠券
* @param {Object} coupon_type_id
*/
export function closeCoupon(coupon_type_id) {
return request.post('/coupon/storeapi/coupon/close', {data: {coupon_type_id}})
}
/**
* 删除优惠券
* @param {Object} coupon_type_id
*/
export function deleteCoupon(coupon_type_id) {
return request.post('/coupon/storeapi/coupon/delete', {data: {coupon_type_id}})
}

View File

@@ -0,0 +1,138 @@
import request from '@/common/js/http'
/**
* 获取会员信息
* @param {Object} member_id
*/
export function getMemberInfoById(member_id) {
return request.post('/cashier/storeapi/member/info', {data: {member_id}})
}
/**
* 获取会员信息通过search_text
* @param params
* @returns {*}
*/
export function getMemberInfoBySearchMember(params) {
return request.post('/cashier/storeapi/member/searchmember', {data: params})
}
/**
* 获取会员列表
* @param {Object} params
*/
export function getMemberList(params) {
return request.post('/cashier/storeapi/member/lists', {data: params})
}
/**
* 获取会员等级
*/
export function getMemberLevelList() {
return request.post('/cashier/storeapi/memberlevel/lists')
}
/**
* 添加会员
* @param {Object} params
*/
export function addMember(params) {
return request.post('/cashier/storeapi/member/addmember', {data: params})
}
/**
* 编辑会员
* @param {Object} params
*/
export function editMember(params) {
return request.post('/cashier/storeapi/member/editmember', {data: params})
}
/**
* 获取会员卡包列表
* @param {Object} params
*/
export function getMemberCardList(params) {
return request.post('/cardservice/storeapi/membercard/lists', {data: params})
}
/**
* 获取会员卡包详情
* @param {Object} params
*/
export function getMemberCardDetail(params) {
return request.post('/cardservice/storeapi/membercard/detail', {data: params})
}
/**
* 获取可发放优惠券列表
* @param {Object} params
*/
export function getCouponTypeList(params) {
return request.post('/coupon/storeapi/coupon/getStoreCouponTypeList', {data: params})
}
/**
* 发放优惠券
* @param {Object} params
*/
export function sendMemberCoupon(params) {
return request.post('/cashier/storeapi/member/sendCoupon', {data: params})
}
/**
* 调整会员积分
* @param {Object} params
*/
export function modifyMemberPoint(params) {
return request.post('/cashier/storeapi/member/modifypoint', {data: params})
}
/**
* 调整会员余额
* @param {Object} params
*/
export function modifyMemberBalance(params) {
return request.post('/cashier/storeapi/member/modifybalance', {data: params})
}
/**
* 调整会员成长值
* @param {Object} params
*/
export function modifyMemberGrowth(params) {
return request.post('/cashier/storeapi/member/modifygrowth', {data: params})
}
/**
* 办理会员卡
* @param {Object} params
*/
export function applyingMembershipCard(params) {
return request.post('/cashier/storeapi/member/handleMember', {data: params})
}
/**
* 发送短信验证码
* @param {Object} member_id
*/
export function sendMemberVerifyCode(member_id) {
return request.post('/cashier/storeapi/member/memberverifycode', {data: {member_id}})
}
/**
* 验证短信验证码
* @param {Object} params
*/
export function checkMemberVerifyCode(params) {
return request.post('/cashier/storeapi/member/checksmscode', {data: params})
}
/**
* 根据手机号查询会员,支持模糊
* @param params
* @returns {*}
*/
export function searchMemberByMobile(params) {
return request.post('/cashier/storeapi/member/searchMemberByMobile', {data: params})
}

View File

@@ -0,0 +1,55 @@
import request from '@/common/js/http'
/**
* 支付确认
* @param {Object} params
*/
export function cashierConfirm(params) {
return request.post('/cashier/storeapi/cashierpay/confirm', {data: params})
}
/**
* 创建支付单
* @param {Object} out_trade_no
*/
export function addPayCashierPay(out_trade_no) {
return request.post('/cashier/storeapi/cashierpay/createpay', {data: {out_trade_no}})
}
/**
* 获取支付二维码
* @param {Object} out_trade_no
*/
export function getPayQrcode(out_trade_no) {
return request.post('/cashier/storeapi/cashierpay/payqrcode', {data: {out_trade_no}})
}
/**
* 获取扫码枪配置
* */
export function getPayType() {
return request.post('/cashier/storeapi/Cashierpay/payType')
}
/**
* 扫码枪支付
* @param {Object} params
*/
export function authCodepay(params) {
return request.post('/pay/pay/authCodepay', {data: params})
}
/**
* 获取支付信息
* @param {Object} out_trade_no
*/
export function getCashierPayInfo(out_trade_no) {
return request.post('/cashier/storeapi/cashierpay/info', {data: {out_trade_no}})
}
/**
* 账号余额支付确认
* @param {Object} params
*/
export function checkPaymentCode(params) {
return request.post('/cashier/storeapi/member/checkpaymentcode', {data: params})
}

View File

@@ -0,0 +1,129 @@
import request from '@/common/js/http'
import util from '@/common/js/util'
/**
* 订单列表
* @param {Object} params
*/
export function getOrderList(params) {
return request.post('/cashier/storeapi/cashierorder/lists', {
data: params
})
}
/**
* 订单详情
* @param {Object} params
*/
export function getOrderDetail(params) {
return request.post('/cashier/storeapi/cashierorder/detail', {
data: params
})
}
/**
* 获取支付信息
* @param {Object} order_id
*/
export function getOrderInfoById(order_id) {
return request.post('/cashier/storeapi/order/info', {
data: {order_id}
})
}
/**
* 订单调价
* @param {Object} params
*/
export function orderAdjustPrice(params) {
return request.post('/cashier/storeapi/cashierorder/adjustPrice', {
data: params
})
}
/**
* 订单备注
* @param {Object} params
*/
export function orderRemark(params) {
return request.post('/cashier/storeapi/cashierorder/orderRemark', {
data: params
})
}
/**
* 提货
* @param {Object} order_id
*/
export function orderStoreDelivery(order_id) {
return request.post('/cashier/storeapi/order/storedelivery', {
data: {order_id}
})
}
/**
* 本地配送
* @param {Object} params
*/
export function orderLocalDelivery(params) {
return request.post('/cashier/storeapi/order/localdelivery', {
data: params
})
}
/**
* 订单关闭
* @param {Object} params
*/
export function orderClose(params) {
return request.post('/cashier/storeapi/order/close', {
data: params
})
}
/**
* 打印小票
* @param order_id
* @returns {*}
*/
export function orderPrintTicket(order_id) {
let params = {order_id:order_id};
//追加打印机数据
let local_config = util.getLocalConfig();
params.printer_ids = local_config.printerSelectType == 'all' ? 'all' : local_config.printerSelectIds.toString();
return request.post('/cashier/storeapi/cashierorder/printticket', {
data: params
})
}
/**
* 获取物流公司
*/
export function getExpressCompanyList() {
return request.post('/cashier/storeapi/order/expresscompany')
}
/**
* 获取配送员列表
* @param {Object} params
*/
export function getOrderDeliverList(params) {
return request.post('/cashier/storeapi/order/deliverlist', {data: params})
}
/**
* 订单删除
* @param {Object} params
*/
export function orderExpressDelivery(params) {
return request.post('/cashier/storeapi/order/expressdelivery', {
data: params
})
}
/**
* 获取筛选条件
*/
export function getorderCondition() {
return request.post('/cashier/storeapi/order/condition')
}

View File

@@ -0,0 +1,41 @@
import request from '@/common/js/http'
/**
* 开单(计算)
* @param {Object} params
*/
export function calculate(params) {
return request.post('/cashier/storeapi/cashierordercreate/calculate', {data: params})
}
/**
* 开单(订单创建)
* @param {Object} params
*/
export function create(params) {
return request.post('/cashier/storeapi/cashierordercreate/create', {data: params})
}
/**
* 售卡(计算)
* @param {Object} params
*/
export function cardCalculate(params) {
return request.post('/cashier/storeapi/cashierordercreate/cardcalculate', {data: params})
}
/**
* 售卡(订单创建)
* @param {Object} params
*/
export function cardCreate(params) {
return request.post('/cashier/storeapi/cashierordercreate/cardcreate', {data: params})
}
/**
* 支付(计算)
* @param {Object} params
*/
export function payCalculate(params) {
return request.post('/cashier/storeapi/cashierpay/paycalculate', {data: params})
}

View File

@@ -0,0 +1,89 @@
import request from '@/common/js/http'
/**
* 订单退款申请数据
* @param {Object} params
*/
export function getRefundApplyData(params) {
return request.post('/cashier/storeapi/cashierorderrefund/getrefundapplydata', {
data: params
})
}
/**
* 订单退款
* @param {Object} params
*/
export function orderRefund(params) {
return request.post('/cashier/storeapi/cashierorderrefund/refund', {
data: params
})
}
/**
* 转账
* @param {Object} params
*/
export function orderRefundComplete(params) {
return request.post('/cashier/storeapi/cashierorderrefund/complete', {
data: params
})
}
/**
* 获取订单列表
* @param {Object} params
*/
export function getOrderRefundLists(params) {
return request.post('/cashier/storeapi/cashierorderrefund/lists', {
data: params
})
}
/**
* 获取订单详情
* @param {Object} params
*/
export function getOrderRefundDetail(params) {
return request.post('/cashier/storeapi/cashierorderrefund/detail', {data: params})
}
/**
* 同意维权
* @param {Object} params
*/
export function orderRefundAgree(params) {
return request.post('/cashier/storeapi/cashierorderrefund/agree', {
data: params
})
}
/**
* 拒绝维权
* @param {Object} params
*/
export function orderRefundRefuse(params) {
return request.post('/cashier/storeapi/cashierorderrefund/refuse', {
data: params
})
}
/**
* 买家退货接收,维权收货
* @param {Object} params
*/
export function orderRefundReceive(params) {
return request.post('/cashier/storeapi/cashierorderrefund/receive', {
data: params
})
}
/**
* 关闭维权
* @param {Object} params
*/
export function orderRefundClose(params) {
return request.post('/cashier/storeapi/cashierorderrefund/close', {
data: params
})
}

View File

@@ -0,0 +1,41 @@
import request from '@/common/js/http'
/**
* 挂单
* @param {Object} params
*/
export function addPendOrder(params) {
return request.post('/cashier/storeapi/pendorder/add', {data: params})
}
/**
* 取单
* @param {Object} params
*/
export function editPendOrder(params) {
return request.post('/cashier/storeapi/pendorder/edit', {data: params})
}
/**
* 修改备注
* @param {Object} params
*/
export function editPendOrderRemark(params) {
return request.post('/cashier/storeapi/pendorder/updateremark', {data: params})
}
/**
* 获取挂单
* @param {Object} params
*/
export function getPendOrderList(params) {
return request.post('/cashier/storeapi/pendorder/page', {data: params})
}
/**
* 删除挂单
* @param {Object} order_id
*/
export function deletePendOrder(order_id) {
return request.post('/cashier/storeapi/pendorder/delete', {data: {order_id}})
}

View File

@@ -0,0 +1,61 @@
import request from '@/common/js/http'
import util from '@/common/js/util'
/**
* 获取打印机列表
*/
export function getPrinterList(params) {
return request.post('/printer/storeapi/printer/lists', {data: params})
}
/**
* 获取打印机模板
*/
export function getTemplate() {
return request.post('/printer/storeapi/printer/template')
}
/**
* 获取打印机详情
*/
export function getPrinterInfo(printer_id) {
return request.post('/printer/storeapi/printer/info', {data: {printer_id}})
}
/**
* 删除打印机
*/
export function deletePrinter(printer_id) {
return request.post('/printer/storeapi/printer/deleteprinter', {data: {printer_id}})
}
/**
* 获取订单类型
*/
export function getOrderType() {
return request.post('/printer/storeapi/printer/getordertype')
}
/**
* 编辑打印机
*/
export function editPrinter(params) {
return request.post('/printer/storeapi/printer/edit', {data: params})
}
/**
* 添加打印机
*/
export function addPrinter(params) {
return request.post('/printer/storeapi/printer/add', {data: params})
}
/**
* 打印小票
*/
export function printTicket(params = {}) {
//追加打印机数据
let local_config = util.getLocalConfig();
params.printer_ids = local_config.printerSelectType == 'all' ? 'all' : local_config.printerSelectIds.toString();
return request.post('/cashier/storeapi/cashier/printticket', {data: params})
}

View File

@@ -0,0 +1,15 @@
import request from '@/common/js/http'
/**
* 获取推广二维码设置
*/
export function getAddonIsExist() {
return request.post('/cashier/storeapi/Config/addonIsExist')
}
/**
* 获取推广二维码
* @param {Object} params
*/
export function getPromotionQrcode(params) {
return request.post('/cashier/storeapi/Promotion/getPromotionQrcode', {data: params})
}

View File

@@ -0,0 +1,35 @@
import request from '@/common/js/http'
/**
* 获取订单列表
* @param {Object} params
*/
export function getOrderRechargeList(params) {
return request.post('/cashier/storeapi/recharge/orderpage', {
data: params
})
}
/**
* 获取订单详情
* @param {Object} params
*/
export function getOrderRechargeDetail(params) {
return request.post('/cashier/storeapi/recharge/orderdetail', {data: params})
}
/**
* 获取充值基本配置
* @param {Object} params
*/
export function getRechargeConfig(params) {
return request.post('/cashier/storeapi/recharge/activity', {data: params})
}
/**
* 客户充值
* @param {Object} params
*/
export function addRecharge(params) {
return request.post('/cashier/storeapi/cashierordercreate/rechargecreate', {data: params})
}

View File

@@ -0,0 +1,112 @@
import request from '@/common/js/http'
/**
* 获取预约配置
*/
export function getReserveConfig() {
return request.post('/cardservice/storeapi/reserve/getConfig')
}
/**
* 保存预约配置
* @param {Object} params
*/
export function setReserveConfig(params) {
return request.post('/cardservice/storeapi/reserve/setConfig', {data: params})
}
/********************** 预约 ***********************/
/**
* 获取预约状态字典
*/
export function getReserveStatus() {
return request.post('/cardservice/storeapi/reserve/status')
}
/**
* 获取预约记录(每周)
* @param {Object} params
*/
export function getReserveWeekday(params) {
return request.post('/cardservice/storeapi/reserve/getweekday', {data: params})
}
/**
* 获取预约分页数据
* @param {Object} params
*/
export function getReserveLists(params) {
return request.post('/cardservice/storeapi/reserve/lists', {data: params})
}
/**
* 获取预约项目列表
* @param {Object} params
*/
export function getAppointmentProjectList(params) {
return request.post('/cardservice/storeapi/reserve/servicelist', {data: params})
}
/**
* 获取员工列表
*/
export function getEmployeeList() {
return request.post('/cardservice/storeapi/reserve/servicer')
}
/**
* 获取预约详情
* @param reserve_id
*/
export function getReserveDetail(reserve_id) {
return request.post('/cardservice/storeapi/reserve/detail', {data: {reserve_id}})
}
/**
* 添加预约
* @param {Object} params
*/
export function addReserve(params) {
return request.post('/cardservice/storeapi/reserve/add', {data: params})
}
/**
* 编辑预约
* @param {Object} params
*/
export function editReserve(params) {
return request.post('/cardservice/storeapi/reserve/update', {data: params})
}
/**
* 取消预约
* @param reserve_id
*/
export function cancelReserve(reserve_id) {
return request.post('/cardservice/storeapi/reserve/cancel', {data: reserve_id})
}
/**
* 预约到店确认
* @param reserve_id
*/
export function reserveToStore(reserve_id) {
return request.post('/cardservice/storeapi/reserve/confirmToStore', {data: {reserve_id}})
}
/**
* 预约确认
* @param reserve_id
*/
export function reserveConfirm(reserve_id) {
return request.post('/cardservice/storeapi/reserve/confirm', {data: {reserve_id}})
}
/**
* 预约完成
* @param reserve_id
*/
export function reserveComplete(reserve_id) {
return request.post('/cardservice/storeapi/reserve/complete', {data: {reserve_id}})
}

View File

@@ -0,0 +1,43 @@
import request from '@/common/js/http'
/**
* 获取电子秤列表
*/
export function getScaleList(params) {
return request.post('/scale/storeapi/scale/page', {data: params})
}
/**
* 获取电子秤详情
*/
export function getScaleDetail(params) {
return request.post('/scale/storeapi/scale/detail', {data: params})
}
/**
* 删除电子秤
*/
export function deleteScale(params) {
return request.post('/scale/storeapi/scale/delete', {data: params})
}
/**
* 获取电子秤品牌
*/
export function getScaleBrand() {
return request.post('/scale/storeapi/scale/scaleBrand')
}
/**
* 编辑电子秤
*/
export function editScale(params) {
return request.post('/scale/storeapi/scale/edit', {data: params})
}
/**
* 添加电子秤
*/
export function addScale(params) {
return request.post('/scale/storeapi/scale/add', {data: params})
}

View File

@@ -0,0 +1,67 @@
import request from '@/common/js/http'
/**
* 获取提现设置
*/
export function getWithdrawConfig() {
return request.post('/store/storeapi/store/withdrawconfig')
}
/**
* 申请提现
* @param {Object} money
*/
export function applyWithdraw(money) {
return request.post('/store/storeapi/withdraw/apply', {data: {money}})
}
/**
* 提现详情
* @param {Object} withdraw_id
*/
export function withdrawDetail(withdraw_id) {
return request.post('/store/storeapi/withdraw/detail', {data: {withdraw_id}})
}
/**
* 获取提现方式
*/
export function getWithdrawScreen() {
return request.post('/store/storeapi/withdraw/screen')
}
/**
* 获取提现列表
* @param {Object} params
*/
export function getWithdrawPage(params) {
return request.post('/store/storeapi/withdraw/page', {data: params})
}
/**
* 账户筛选条件
*/
export function getAccountScreen() {
return request.post('/store/storeapi/account/screen')
}
/**
* 账户导出
*/
export function accountExport(params) {
return request.post('/store/storeapi/account/export', {data: params})
}
/**
* 是否可以新版本提现
*/
export function withdrawConfig() {
return request.post('/wechatpay/api/transfer/getWithdrawConfig')
}
/**
* 提现二维码
*/
export function transferCode(params) {
return request.post('/store/storeapi/withdraw/getTransferCode', {data: params})
}

View File

@@ -0,0 +1,22 @@
import request from '@/common/js/http'
/**
* 收银交班
*/
export function changeShifts(params = {}) {
return request.post('/cashier/storeapi/cashier/changeshifts', {data: params})
}
/**
* 数量统计
*/
export function getShiftsData() {
return request.post('/cashier/storeapi/cashier/shiftsdata')
}
/**
* 导出交班记录
*/
export function saleGoodsExport(params) {
return request.post('/cashier/storeapi/cashier/changeShiftsSaleGoodsExport', {data: params})
}

View File

@@ -0,0 +1,29 @@
import request from '@/common/js/http'
/**
* 营业数据统计相关
*/
/**
* 获取整体统计
* @param {Object} params
*/
export function getStatTotal(params) {
return request.post('/cashier/storeapi/stat/statTotal', {data: params})
}
/**
* 获取当日统计
* @param {Object} params
*/
export function getStatDay(params) {
return request.post('/cashier/storeapi/stat/dayStatData', {data: params})
}
/**
* 获取当日统计
* @param {Object} params
*/
export function getStatHour(params) {
return request.post('/cashier/storeapi/stat/hourStatData', {data: params})
}

View File

@@ -0,0 +1,292 @@
import request from '@/common/js/http'
/**
* 获取库存商品列表
* @param {Object} params
*/
export function getStockGoodsList(params) {
return request.post('/stock/storeapi/manage/lists', {data: params})
}
/**
* 获取商品的库存流水
* @param {Object} params
*/
export function getStockGoodsRecords(params) {
return request.post('/stock/storeapi/manage/records', {data: params})
}
/**
* 获取单据类型
*/
export function getDocumentType() {
return request.post('/stock/storeapi/manage/getDocumentType')
}
/************************** 库存通用接口 ********************************/
/**
* 出入库单据审核通过
* @param document_id
*/
export function storageAgree(document_id) {
return request.post('/stock/storeapi/storage/agree', {data: {document_id}})
}
/**
* 入库单据审核拒绝
* @param {Object} params 需要包括拒绝理由
*/
export function storageRefuse(params) {
return request.post('/stock/storeapi/storage/refuse', {data: params})
}
/**
* 出入库单据删除
* @param {Object} document_id
*/
export function storageDelete(document_id) {
return request.post('/stock/storeapi/storage/delete', {data: {document_id}})
}
/**
* 库存回车查询单条商品
* @param {Object} params
*/
export function getSkuListForStock(params) {
return request.post('/stock/storeapi/manage/getskulist', {data: params})
}
/************************** 入库接口 ********************************/
/**
* 获取入库单列表
* @param {Object} params
*/
export function getStorageLists(params) {
return request.post('/stock/storeapi/storage/lists', {data: params})
}
/**
* 获取入库单详情
* @param document_id
*/
export function getStorageDetail(document_id) {
return request.post('/stock/storeapi/storage/detail', {data: {document_id}})
}
/**
* 获取入库单号
*/
export function getStorageDocumentNo() {
return request.post('/stock/storeapi/storage/getDocumentNo')
}
/**
* 获取入库单编辑时详情
* @param document_id
*/
export function getStorageDetailInEdit(document_id) {
return request.post('/stock/storeapi/storage/editData', {data: {document_id}})
}
/**
* 入库单编辑新增
* @param {Object} params
*/
export function editStorage(params) {
return request.post('/stock/storeapi/storage/stockin', {data: params})
}
/************************** 出库接口 ********************************/
/**
* 获取出库单列表
* @param {Object} params
*/
export function getWastageLists(params) {
return request.post('/stock/storeapi/wastage/lists', {data: params})
}
/**
* 获取出库单详情
* @param document_id
*/
export function getWastageDetail(document_id) {
return request.post('/stock/storeapi/wastage/detail', {data: {document_id}})
}
/**
* 获取出库单号
*/
export function getWastageDocumentNo() {
return request.post('/stock/storeapi/wastage/getDocumentNo')
}
/**
* 获取出库单编辑时详情
* @param document_id
*/
export function getWastageDetailInEdit(document_id) {
return request.post('/stock/storeapi/wastage/editData', {data: {document_id}})
}
/**
* 出库单编辑新增
* @param {Object} params
*/
export function editWastage(params) {
return request.post('/stock/storeapi/wastage/stockout', {data: params})
}
/************************** 调拨接口 ********************************/
/**
* 获取调拨单列表
* @param {Object} params
*/
export function getAllocateList(params) {
return request.post('/stock/storeapi/allocate/lists', {data: params})
}
/**
* 获取调拨单详情
* @param {Object} allot_id
*/
export function getAllocateDetail(allot_id) {
return request.post('/stock/storeapi/allocate/detail', {data: {allot_id: allot_id}})
}
/**
* 获取调拨单号
*/
export function getAllotNo() {
return request.post('/stock/storeapi/allocate/getAllotNo')
}
/**
* 获取调拨单编辑时详情
* @param allot_id
*/
export function getAllocateDetailInEdit(allot_id) {
return request.post('/stock/storeapi/allocate/editData', {data: {allot_id}})
}
/**
* 获取调拨门店列表(库存查询门店)
*/
export function getStoreLists() {
return request.post('/stock/storeapi/store/lists')
}
/**
* 调拨单新增
* @param {Object} params
*/
export function addAllocate(params) {
return request.post('/stock/storeapi/allocate/addallocate', {data: params})
}
/**
* 调拨单编辑
* @param {Object} params
*/
export function editAllocate(params) {
return request.post('/stock/storeapi/allocate/editAllocate', {data: params})
}
/**
* 调拨单据删除
* @param {Object} allot_id
*/
export function allocateDelete(allot_id) {
return request.post('/stock/storeapi/allocate/delete', {data: {allot_id: allot_id}})
}
/**
* 调拨单据审核通过
* @param {Object} allot_id
*/
export function allocateAgree(allot_id) {
return request.post('/stock/storeapi/allocate/agree', {data: {allot_id: allot_id}})
}
/**
* 调拨单据审核拒绝
* @param {Object} params 需要包括拒绝理由
*/
export function allocateRefuse(params) {
return request.post('/stock/storeapi/allocate/refuse', {data: params})
}
/************************** 库存盘点接口 ********************************/
/**
* 获取盘点单列表
* @param {Object} params
*/
export function getInventoryList(params) {
return request.post('/stock/storeapi/check/lists', {data: params})
}
/**
* 获取盘点单详情
* @param {Object} inventory_id
*/
export function getInventoryDetail(inventory_id) {
return request.post('/stock/storeapi/check/detail', {data: {inventory_id}})
}
/**
* 获取盘点单号
*/
export function getInventoryNo() {
return request.post('/stock/storeapi/Check/getInventoryNo')
}
/**
* 获取盘点单编辑时详情
* @param inventory_id
*/
export function getInventoryDetailInEdit(inventory_id) {
return request.post('/stock/storeapi/check/editData', {data: {inventory_id}})
}
/**
* 盘点单新增
* @param {Object} params
*/
export function addInventory(params) {
return request.post('/stock/storeapi/check/add', {data: params})
}
/**
* 盘点单编辑
* @param {Object} params
*/
export function editInventory(params) {
return request.post('/stock/storeapi/check/edit', {data: params})
}
/**
* 盘点单据删除
* @param {Object} inventory_id
*/
export function inventoryDelete(inventory_id) {
return request.post('stock/storeapi/check/delete', {data: {inventory_id}})
}
/**
* 盘点单据审核通过
* @param {Object} inventory_id
*/
export function inventoryAgree(inventory_id) {
return request.post('/stock/storeapi/check/agree', {data: {inventory_id}})
}
/**
* 盘点单据审核拒绝
* @param {Object} params 需要包括拒绝理由
*/
export function inventoryRefuse(params) {
return request.post('/stock/storeapi/check/refuse', {data: params})
}

View File

@@ -0,0 +1,37 @@
import request from '@/common/js/http'
/**
* 获取门店信息(当前)
*/
export function getStoreInfo() {
return request.post('/cashier/storeapi/store/info')
}
/**
* 获取门店列表(当前用户有权限的)
*/
export function getStoreList() {
return request.post('/cashier/storeapi/store/lists')
}
/**
* 门店编辑
* @param {Object} params
*/
export function editStore(params) {
return request.post('/store/storeapi/store/edit', {data: params})
}
/**
* 获取所有门店标签
*/
export function getAllStoreLabel() {
return request.post('/store/storeapi/store/label')
}
/**
* 获取所有的门店分类
*/
export function getAllStoreCategory() {
return request.post('/store/storeapi/store/category')
}

View File

@@ -0,0 +1,57 @@
import request from '@/common/js/http'
/**
* 获取员工列表
* @param {Object} params
*/
export function getUserList(params) {
return request.post('/cashier/storeapi/user/lists', {data: params})
}
/**
* 获取员工详情
* @param {Object} uid
*/
export function getUserDetail(uid) {
var params = {};
if (uid) params.uid = uid;
return request.post('/cashier/storeapi/user/userinfo', {data: params})
}
/**
* 获取所有用户组(角色)
*/
export function getAllGroups() {
return request.post('/cashier/storeapi/user/group')
}
/**
* 添加员工
* @param {Object} params
*/
export function addUser(params) {
return request.post('/cashier/storeapi/user/adduser', {data: params})
}
/**
* 编辑员工
* @param {Object} params
*/
export function editUser(params) {
return request.post('/cashier/storeapi/user/edituser', {data: params})
}
/**
* 删除员工
* @param {Object} uid
*/
export function deleteUser(uid) {
return request.post('/cashier/storeapi/user/deleteuser', {data: {uid}})
}
/**
* 获取门店用户权限
*/
export function getUserGroupAuth() {
return request.post('/cashier/storeapi/user/usergroupauth')
}

View File

@@ -0,0 +1,33 @@
import request from '@/common/js/http'
/**
* 获取核销码信息
* @param {Object} code
*/
export function getVerifyInfo(code) {
return request.post('/cashier/storeapi/verify/info', {data: {code: code}})
}
/**
* 核销操作
* @param {Object} code
*/
export function verifyCode(code) {
return request.post('/cashier/storeapi/verify/verify', {data: {verify_code: code}})
}
/**
* 核销记录列表
* @param {Object} params
*/
export function getVerifyRecordList(params) {
return request.post('/cashier/storeapi/verify/recordlists', {data: params})
}
/**
* 核销记录列表
* @param {Object} id
*/
export function getVerifyRecordDetail(id) {
return request.post('/cashier/storeapi/verify/recordsdetail', {data: {id: id}})
}

View File

@@ -0,0 +1,751 @@
// 基础flex布局
.uni-flex {
display: flex;
flex-direction: row;
}
.uni-flex-item {
flex: 1;
}
.uni-row {
flex-direction: row;
}
.uni-column {
flex-direction: column;
}
.w-full {
width: 100%;
}
.flex-1 {
flex: 1;
}
.justify-start {
-webkit-box-pack: start;
-ms-flex-pack: start;
-webkit-justify-content: flex-start;
justify-content: flex-start;
}
.justify-between {
justify-content: space-between;
}
.justify-center{
justify-content: center;
}
.items-center {
align-items: center;
}
.text-color {
color: $primary-color;
}
.items-end {
align-items: end;
}
.items-baseline {
align-items: baseline;
}
.items-flex-end {
align-items: flex-end;
}
.flex-shrink-0 {
-ms-flex-negative: 0;
-webkit-flex-shrink: 0;
flex-shrink: 0;
}
.flex-grow-0 {
-webkit-box-flex: 0;
-ms-flex-positive: 0;
-webkit-flex-grow: 0;
flex-grow: 0;
}
.self-end {
align-self: flex-end;
}
.justify-self-auto {
-ms-grid-column-align: auto;
justify-self: auto;
}
.flex-col {
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
-webkit-flex-direction: column;
flex-direction: column;
}
.content-start {
-ms-flex-line-pack: start;
-webkit-align-content: flex-start;
align-content: flex-start;
}
.flex-wrap {
flex-wrap: wrap;
}
.justify-end {
-webkit-box-pack: end;
-ms-flex-pack: end;
-webkit-justify-content: flex-end;
justify-content: flex-end;
}
// 高度填充
.height-all {
min-height: 100%;
}
// 公共区块样式
.common-wrap {
box-sizing: border-box;
background: #fff;
// margin: .15rem .2rem;
// &:first-child {
// margin-right: 0;
// }
// &:last-child {
// margin-right: .14rem;
// }
}
// 公共滚动条样式
.common-scrollbar,
.common-scrollbar .uni-scroll-view {
&::-webkit-scrollbar {
width: 0.06rem;
height: 0.06rem;
background-color: rgba($color: #000000, $alpha: 0);
}
&::-webkit-scrollbar-button {
display: none;
}
&::-webkit-scrollbar-thumb {
border-radius: 0.06rem;
box-shadow: inset 0 0 0.06rem rgba(45, 43, 43, 0.45);
background-color: #ddd;
}
&::-webkit-scrollbar-track {
background-color: transparent;
}
}
// 公共头像样式
.common-headimg {
overflow: hidden;
border-radius: 50%;
image {
width: 100%;
}
}
// 公共tab切换
.common-tab-wrap {
height: 0.53rem;
border-bottom: 0.01rem solid #e6e6e6;
display: flex;
padding: 0 0.2rem;
position: relative;
.tab-item {
height: 0.53rem;
line-height: 0.53rem;
padding: 0 0.2rem;
cursor: pointer;
&:first-child {
padding-left: 0;
}
&.active-bar {
color: $primary-color;
}
}
.active {
position: absolute;
width: 0;
height: 0.03rem;
border-radius: 0.02rem;
background: $primary-color;
bottom: -0.01rem;
left: 0;
transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
}
// 公共表格操作样式
.common-table-action {
text {
margin-left: 0.1rem;
color: $primary-color;
cursor: pointer;
transition: all 0.3s;
&:hover {
color: var(--primary-color-light-2);
}
}
}
// 公共表格批量操作样式
.common-table-batch {
display: flex;
button {
width: auto !important;
}
}
button {
border-radius: 0.02rem;
box-sizing: border-box;
&::after {
border-radius: 0.02rem;
box-sizing: border-box;
}
}
// 主要按钮
.primary-btn {
background: $primary-color !important;
color: #fff !important;
font-size: 0.14rem;
&::after {
border-width: 0;
}
&:hover {
background-color: var(--primary-color-light-2) !important;
}
&[disabled] {
background: var(--primary-color-light-6) !important;
border-color: var(--primary-color-light-6) !important;
color: #fff !important;
&:hover {
background: var(--primary-color-light-6) !important;
border-color: var(--primary-color-light-6) !important;
color: #fff !important;
}
}
}
// 默认按钮
.default-btn {
font-size: 0.14rem;
background: #fff !important;
color: #606266 !important;
border-color: #606266 !important;
&:hover {
color: $primary-color !important;
border-color: $primary-color !important;
}
&:hover:after {
border-color: $primary-color !important;
}
&[plain] {
color: $primary-color !important;
border-color: $primary-color !important;
background-color: var(--primary-color-light-9) !important;
&:after {
border-color: $primary-color !important;
}
&:hover {
background: $primary-color !important;
color: #fff !important;
}
&[disabled] {
color: #ddd !important;
border-color: #e6e6e6 !important;
background: #f5f5f5 !important;
&:after {
border-color: #e6e6e6 !important;
}
&:hover {
color: #ddd !important;
border-color: #e6e6e6 !important;
}
}
}
&[disabled] {
color: #ddd !important;
border-color: #e6e6e6 !important;
background: #f5f5f5 !important;
&:after {
border-color: #e6e6e6 !important;
}
&:hover {
color: #ddd !important;
border-color: #e6e6e6 !important;
}
}
}
/* 公共数字键盘 */
.keyboard-wrap {
width: 5rem;
margin-top: 0.2rem;
display: flex;
.num-wrap {
flex: 1;
width: 0;
display: flex;
flex-wrap: wrap;
justify-content: center;
.key-item {
margin: 0.1rem 0.1rem 0 0;
background: #f5f5f5;
min-width: calc((100% - 0.3rem) / 3);
text-align: center;
padding: 0.15rem 0;
border-radius: 0.05rem;
font-size: 0.16rem;
font-weight: bold;
transition: all 0.3s;
cursor: pointer;
&:hover {
background: #ddd;
}
&.empty:hover {
background: #f5f5f5;
}
}
}
.action-wrap {
display: flex;
flex-direction: column;
width: 1rem;
.delete,
.confirm {
background: #f5f5f5;
min-width: calc((100% - 0.3rem) / 3);
text-align: center;
padding: 0.15rem 0;
margin-top: 0.1rem;
border-radius: 0.05rem;
font-size: 0.16rem;
cursor: pointer;
transition: all 0.3s;
&:hover {
background: #ddd;
}
}
.confirm {
flex: 1;
background: $primary-color;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
&:hover {
background: var(--primary-color-light-1);
}
}
}
}
.cursor-pointer {
cursor: pointer;
}
/* 单行超出隐藏 */
.using-hidden {
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
white-space: break-spaces;
}
/* 多行超出隐藏 */
.multi-hidden {
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.w-full {
width: 100%;
}
.h-full {
height: 100%;
}
.flex {
display: flex;
}
.overflow-ellipsis {
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
}
.truncate {
overflow: hidden;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
}
button::after {
-webkit-transform: none;
transform: none;
width: 100%;
height: 100%;
border-radius: 0;
}
button[type=default] {
background-color: #fff;
}
.border-0 {
border-width: 0 !important;
}
uni-switch .uni-switch-input.uni-switch-input-checked {
background-color: $primary-color;
border-color: $primary-color;
}
//二次确认弹框
.confirm-pop {
width: 3rem;
// min-height: 1.5rem;
border-radius: 0.06rem;
background: #ffffff;
box-sizing: border-box;
padding: 0.2rem;
.title {
font-size: 0.16rem;
text-align: center;
}
.btn {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
margin-top: 0.3rem;
.btn {
width: auto;
padding: 0 0.15rem;
margin: 0;
height: 0.35rem;
flex: 1;
}
.btn:last-child {
margin-left: 0.1rem;
}
}
&.message {
width: 5.2rem;
min-height: 3.2rem;
background: #ffffff;
padding-top: 0;
padding-bottom: 0.15rem;
&:after {
overflow: hidden;
display: block;
content: '';
height: 0;
clear: both;
}
.title {
width: 100%;
height: 0.5rem;
border-bottom: 0.01rem solid #e6e6e6;
text-align: center;
line-height: 0.5rem;
font-size: 0.16rem;
font-weight: bold;
position: relative;
.iconguanbi1 {
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 0.15rem;
font-size: 0.18rem;
}
}
.textarea-box {
margin: 0.15rem;
height: 2.2rem;
border: 0.01rem solid #e6e6e6;
border-radius: 0.06rem;
padding: 0.15rem;
box-sizing: border-box;
.textarea {
width: 100%;
height: 100%;
}
}
.save {
width: calc(100% - 0.3rem);
float: right;
margin: 0.15rem;
box-sizing: border-box;
}
}
}
//开单左侧主体头部会员信息展示样式
.cashregister-header-box {
padding: 0.20rem;
border-bottom: 0.01rem solid #e6e6e6;
padding-bottom: 0.1rem;
.search-box {
.head-search {
display: flex;
.search-switch {
width: 0.7rem;
}
.search-input {
margin-left: 0.1rem;
display: flex;
border-width: 0.01rem;
border-style: solid;
border-color: #e6e6e6;
height: 0.3rem;
align-items: center;
padding-left: 0.05rem;
width: calc(100% - 1.3rem);
.iconfont {
}
input {
margin-left: 0.05rem;
}
}
.search-btn {
width: 0.55rem;
margin-left: 0.1rem;
padding: 0 0.1rem;
height: 0.32rem;
font-size: 0.12rem;
}
}
}
.order-time {
display: flex;
align-items: center;
.title {
user-select: none;
position: relative;
z-index: 3;
height: 0.3rem;
padding: 0 0.1rem 0 0.1rem;
box-sizing: border-box;
border-radius: 0.02rem;
border: 0.01rem solid #e5e5e5;
display: flex;
align-items: center;
font-size: 0.14rem;
color: #333;
}
.uni-date {
flex: 1;
margin-left: 0.1rem;
}
.uni-date-x {
height: 0.28rem;
}
}
.header {
margin-top: 0.14rem;
// height: 0.86rem;
font-size: 0.2rem;
display: flex;
align-items: center;
padding: 0.14rem;
padding-right: 0.12rem;
background: linear-gradient(270deg, #515A6E 0%, #19233E 100%);
border-radius: 0.04rem;
.header-image {
width: 0.48rem;
height: 0.48rem;
border-radius: 50%;
}
.headimg{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.member-nameplate{
width: 0.52rem;
height: 0.18rem;
background-size: 100% 100%;
font-size: 0.1rem;
font-weight: 600;
color: #793913;
line-height: 0.16rem;
text-align: center;
margin-top: -0.09rem;
z-index: 2;
background-image: url('@/static/member/info_bg.png');
}
}
.head-info {
flex: 1;
margin-left: 0.1rem;
.name {
font-size: 0.16rem;
color: #F4C89A;
display: flex;
margin-bottom: 0.04rem;
max-width: 1.6rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.mobile {
width: 0.88rem;
height: 0.2rem;
font-size: 0.14rem;
font-weight: 800;
line-height: 0.2rem;
}
.text {
// max-width: 0.8rem;
font-size: 0.12rem;
line-height: 0.2rem;
display: flex;
align-items: center;
.nickname{
max-width: 0.44rem;
font-size: 0.12rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
line-height: 0.2rem;
}
}
}
.head-info-bottom {
font-size: 0.12rem;
font-weight: 500;
color: #F0F0F0;
line-height: 0.17rem;
&.point{
margin-bottom: 0.02rem;
}
}
}
.switch {
text-align: center;
line-height: 0.3rem;
padding: 0 0.05rem;
overflow: hidden;
font-size: $uni-font-size-sm;
text-overflow: ellipsis;
white-space: nowrap;
-ms-flex-negative: 0;
-webkit-flex-shrink: 0;
flex-shrink: 0;
border-radius: 0.04rem;
&::after{
display: none;
}
}
.menber-open{
width: 0.68rem;
}
.replace-member{
margin-left: 0.06rem;
width: 0.36rem;
}
}
}
//返回上一级
.title-back{
margin-bottom: 0.2rem;
.left{
margin-left: 0.05rem;
}
.iconqianhou1{
font-size: 0.17rem;
}
text{
font-size: 0.18rem !important;
line-height: 1;
}
.content{
margin: -0.02rem 0.05rem 0 0.05rem;
}
}
.bg-primary-color-9{
background-color: var(--primary-color-light-9) !important;
}

View File

@@ -0,0 +1,140 @@
$label-width: 1.1rem;
.common-form{
&.fixd{
padding-bottom: .88rem !important;
.common-btn-wrap{
position: fixed;
z-index: 2;
background-color: #fff;
bottom: 0;
left: .9rem;
right: .07rem;
padding-bottom: .05rem;
display: flex;
align-items: center;
margin-left: 0;
button{
flex: 1;
height: .4rem;
line-height: .4rem;
}
}
}
.common-form-item{
display: flex;
flex-wrap: wrap;
align-items: center;
margin-bottom: .15rem;
.form-label{
padding: .09rem .15rem;
text-align: right;
width: $label-width;
box-sizing: border-box;
.required{
color: red;
margin-right: 0.03rem;
}
}
.form-input{
height: .35rem;
line-height: .35rem;
width: 100%;
padding-left: .1rem;
box-sizing: border-box;
font-size: 0.14rem;
}
.form-inline{
display: flex;
align-items: center;
flex-wrap: wrap;
}
.form-input-inline{
width: 1.9rem;
border: .01rem solid #e6e6e6;
margin-right: 10px;
background-color: #fff;
font-size: 0.14rem;
&.short{
width: .5rem;
}
&.long{
width: 4rem;
}
}
.form-input-block{
flex: 1;
border: .01rem solid #e6e6e6;
background-color: #fff;
}
.form-mid{
margin-right: .1rem;
}
.form-word-aux{
margin-right: .1rem;
color: #999;
font-size: $uni-font-size-base;
}
.form-word-aux-line{
flex-basis: 100%;
color: #999;
margin-left: $label-width;
margin-top: .1rem;
}
/deep/ .input-placeholder{
font-size: $uni-font-size-base;
}
.form-checkbox-group, .form-radio-group{
display: flex;
align-items: center;
}
.form-checkbox-item, .form-radio-item{
margin-right: 26rpx;
display: flex;
align-items: center;
}
/deep/ .uni-radio-input, .uni-checkbox-input{
width: .18rem;
height: .18rem;
}
}
.common-btn-wrap{
margin-left: $label-width;
button{
display: inline-block;
padding: 0 .2rem;
height: .36rem;
line-height: .36rem;
font-size: $uni-font-size-base;
}
.screen-btn{
background-color: $primary-color;
color: #fff;
margin-right: .1rem;
&::after{
border-width: 0;
}
}
}
}
.uni-radio-wrapper{
.uni-radio-input-checked{
background-color: $primary-color!important;
border-color: $primary-color!important;
}
.uni-radio-input:hover{
border-color: $primary-color!important;
}
}
.uni-checkbox-wrapper{
.uni-checkbox-input-checked{
color: $primary-color!important;
}
}
@media (any-hover: hover) {
uni-radio:not([disabled]) .uni-radio-input:hover {
border-color: $primary-color !important;
}
}

View File

@@ -0,0 +1,474 @@
@font-face {
font-family: "iconfont"; /* Project id 3668609 */
src: url('//at.alicdn.com/t/c/font_3668609_3k15vrcn5ip.woff2?t=1731148726122') format('woff2'),
url('//at.alicdn.com/t/c/font_3668609_3k15vrcn5ip.woff?t=1731148726122') format('woff'),
url('//at.alicdn.com/t/c/font_3668609_3k15vrcn5ip.ttf?t=1731148726122') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconshoukuan-:before {
content: "\e645";
}
.icondianzicheng:before {
content: "\edaf";
}
.iconbenji:before {
content: "\e77a";
}
.iconshengyinV6xx1:before {
content: "\e70a";
}
.iconshengyin-jingyinV6xx:before {
content: "\e6ff";
}
.iconwenhao:before {
content: "\e72d";
}
.iconhuaxiangfenxi:before {
content: "\ec14";
}
.iconmenpos:before {
content: "\e652";
}
.iconhuiyuanzhucedengluguanli:before {
content: "\e609";
}
.iconsaomiaoerweima:before {
content: "\e663";
}
.icon31hongbao:before {
content: "\e605";
}
.iconshanchu:before {
content: "\e718";
}
.icontuodong:before {
content: "\e884";
}
.iconhuiyuanma:before {
content: "\e68d";
}
.icona-xingzhuang2:before {
content: "\e679";
}
.iconmima:before {
content: "\e67a";
}
.iconyanzhengma:before {
content: "\e67b";
}
.icondelete:before {
content: "\e615";
}
.iconzhanghuyue:before {
content: "\e60e";
}
.iconshourujiesuan:before {
content: "\e655";
}
.iconqingchushujuku:before {
content: "\e632";
}
.iconpeisong:before {
content: "\e626";
}
.iconyunshangchuan:before {
content: "\e604";
}
.iconnavicon-chps:before {
content: "\e64d";
}
.iconyuyue:before {
content: "\e65f";
}
.icontianmaopeisonganzhuang:before {
content: "\e603";
}
.iconyunshupeisong:before {
content: "\e621";
}
.icontiaobodan:before {
content: "\e670";
}
.iconchukudan:before {
content: "\e671";
}
.iconrukudan:before {
content: "\e674";
}
.iconyuyuedingdan:before {
content: "\e8a2";
}
.iconkucunguanli:before {
content: "\e613";
}
.iconkucunpandian:before {
content: "\e675";
}
.icondayin:before {
content: "\e61d";
}
.icongongyingshang:before {
content: "\e61f";
}
.icondingdandingdanchaxun:before {
content: "\e791";
}
.icondingdan:before {
content: "\e62f";
}
.icon12jiaobanbiao:before {
content: "\e6b1";
}
.iconhexiao:before {
content: "\e606";
}
.iconqia:before {
content: "\e782";
}
.iconicon_yingyongguanli:before {
content: "\eb8f";
}
.iconyuyueguanli:before {
content: "\e62c";
}
.iconkaidan:before {
content: "\e61b";
}
.iconinvisible:before {
content: "\e9af";
}
.iconyanjing5:before {
content: "\e728";
}
.icondianpu:before {
content: "\e66e";
}
.iconxiala:before {
content: "\e6b9";
}
.iconfanhui:before {
content: "\e64a";
}
.iconfuxuankuang1:before {
content: "\e68e";
}
.iconfuxuankuang2:before {
content: "\e68f";
}
.iconcheckbox_weiquanxuan:before {
content: "\e650";
}
.iconwushuju:before {
content: "\e642";
}
.iconqianhou1:before {
content: "\e640";
}
.iconqianhou2:before {
content: "\e641";
}
.iconguanbi:before {
content: "\e600";
}
.iconright-s:before {
content: "\e627";
}
.iconleft-s:before {
content: "\e628";
}
.icongengduo:before {
content: "\e63c";
}
.icon31sousuo:before {
content: "\e601";
}
.iconguanbi1:before {
content: "\e723";
}
.iconsanjiao_xia:before {
content: "\e63d";
}
.iconjifen:before {
content: "\e659";
}
.iconxianjin:before {
content: "\e637";
}
.iconhongbao:before {
content: "\e672";
}
.iconchengchangzhi:before {
content: "\e633";
}
.iconyouhuiquan1:before {
content: "\e6f1";
}
.iconyinhangqia:before {
content: "\e936";
}
.iconwxpay:before {
content: "\e611";
}
.icontubiao_zhifu_zhifubaozhifu:before {
content: "\e690";
}
.iconduigou1:before {
content: "\e64f";
}
.iconchenggong:before {
content: "\e644";
}
.iconfuwuguanli:before {
content: "\e625";
}
.iconliuliangshuju:before {
content: "\e62d";
}
.iconkehuguanli:before {
content: "\e697";
}
.iconqitajilu:before {
content: "\e622";
}
.icondingdanguanli:before {
content: "\e634";
}
.iconcaiwubaobiao:before {
content: "\e6e7";
}
.iconyingyeshujuguanliputong:before {
content: "\e62e";
}
.iconjichuban-sanjidaohang-kehushuju:before {
content: "\e649";
}
.icontubiaozhizuomoban-:before {
content: "\e60c";
}
.iconbangzhu:before {
content: "\e61c";
}
.iconweb-icon-:before {
content: "\e6d6";
}
.icon065chakandingdan:before {
content: "\e643";
}
.icongengduo1:before {
content: "\e73a";
}
.iconsaoyisaosaoma:before {
content: "\e751";
}
.iconjia:before {
content: "\e8e1";
}
.iconfuwudingdan:before {
content: "\e616";
}
.iconchongzhidingdan:before {
content: "\e64b";
}
.iconhuiyuandingdan:before {
content: "\e602";
}
.iconshangpinguanli:before {
content: "\e619";
}
.iconshoukadingdan:before {
content: "\e623";
}
.iconshouyindingdanjine:before {
content: "\e739";
}
.iconyundanguanli:before {
content: "\e6cc";
}
.icongoumaicika:before {
content: "\e673";
}
.iconjishi:before {
content: "\e935";
}
.icontuichu:before {
content: "\e66f";
}
.icondizhi:before {
content: "\e614";
}
.iconshijian:before {
content: "\e629";
}
.iconjian:before {
content: "\e620";
}
.iconweixinzhifu:before {
content: "\e635";
}
.iconjifen1:before {
content: "\e65e";
}
.iconzhifubaozhifu:before {
content: "\e651";
}
.iconyouhui-:before {
content: "\e607";
}
.iconyouhuiquan:before {
content: "\e7d8";
}
.iconhongbao1:before {
content: "\e662";
}
.iconyue:before {
content: "\e6b7";
}
.iconduihao:before {
content: "\e654";
}
.iconsaomaqiang:before {
content: "\e631";
}
.iconjianmianjine:before {
content: "\e6ab";
}
.iconxuanzhong:before {
content: "\e7df";
}
.iconxianjin1:before {
content: "\e62a";
}
.iconyuan_checkbox:before {
content: "\e72f";
}
.iconyuan_checked:before {
content: "\e733";
}
.iconzengpin:before {
content: "\e693";
}
.icontuikuanjilu:before {
content: "\e73b";
}

View File

@@ -0,0 +1,20 @@
var app = {
appkey: 'cashier',
version: '5.5.3',
version_no: '553250709001',
}
var config = {
// 站点ID
siteId: 0,
// api请求地址 https://abc.com
baseUrl: '{{$baseUrl}}',
// 图片域名 https://abc.com
imgDomain: '{{$imgDomain}}',
//长链接 wss://abc.com/wss
webSocket: '{{$webSocket}}',
// app版本信息
app: app,
}
export default config;

View File

@@ -0,0 +1,110 @@
import Config from './config.js'
import Util from './util.js'
const app_type = 'pc';
const app_type_name = 'PC';
export default {
sendRequest(params) {
var method = params.method ?? 'POST', // 请求方式
url = Config.baseUrl + params.url, // 请求路径
data = {
app_type,
app_type_name
};
if (uni.getStorageSync('cashierToken')) data.token = uni.getStorageSync('cashierToken');
if (uni.getStorageSync('siteId')) data.site_id = uni.getStorageSync('siteId');
if (uni.getStorageSync('globalStoreId')) data.store_id = uni.getStorageSync('globalStoreId');
if (params.data != undefined) Object.assign(data, params.data);
if (params.async === false) {
//同步
return new Promise((resolve, reject) => {
uni.request({
url: url,
method: method,
data: data,
header: params.header || {
'content-type': 'application/x-www-form-urlencoded;application/json'
},
dataType: params.dataType || 'json',
responseType: params.responseType || 'json',
success: (res) => {
if (res.data.code == -10009 || res.data.code == -10010) {
uni.removeStorage({
key: 'cashierToken'
});
if (Util.getCurrRoute() != 'pages/login/login') {
Util.redirectTo('/pages/login/login', {}, 'reLaunch');
return;
}
}
resolve(res.data);
},
fail: (res) => {
reject(res);
},
complete: (res) => {
// reject(res);
}
});
})
} else {
//异步
uni.request({
url: url,
method: method,
data: data,
header: params.header || {
'content-type': 'application/x-www-form-urlencoded;application/json'
},
dataType: params.dataType || 'json',
responseType: params.responseType || 'text',
success: (res) => {
if (res.data.code == -10009 || res.data.code == -10010) {
uni.removeStorage({
key: 'cashierToken'
});
if (Util.getCurrRoute() != 'pages/login/login') {
Util.redirectTo('/pages/login/login', {}, 'reLaunch');
return;
}
}
typeof params.success == 'function' && params.success(res.data);
},
fail: (res) => {
typeof params.fail == 'function' && params.fail(res);
},
complete: (res) => {
typeof params.complete == 'function' && params.complete(res);
}
});
}
},
post(url, params) {
const option = {
url,
method: 'post',
async: false
};
return this.sendRequest({
...params,
...option
});
},
get(url, params) {
const option = {
url,
method: 'get',
async: false
};
return this.sendRequest({
...params,
...option
});
}
}

View File

@@ -0,0 +1,150 @@
import {mapGetters} from 'vuex';
export default {
data() {
return {
// 左侧菜单,支持触发的按键集合
menuKeyCode: ['F6', 'F7', 'F8', 'F9', 'F10', 'F11'],
themeColor: ''
};
},
onShow() {
this.setNavigationBarTitleText();
// 监听键盘回调
window.POS_HOTKEY_CALLBACK = (control, code) => {
this.$store.commit('billing/setIsShowCashBox',true);
// 触发左侧菜单按键回调
this.menuTriggerKeyCodeCallBack(code);
};
},
computed: {
cashierToken() {
return uni.getStorageSync('cashierToken');
},
...mapGetters([
'rootSize', 'defaultImg', 'addon', 'menu', 'userInfo', 'themeConfig',
'globalStoreId', 'globalStoreInfo', 'globalMemberId', 'globalMemberInfo'
])
},
watch: {
'globalStoreInfo.store_id': {
handler(nval, oval) {
if (oval && typeof this.switchStoreAfter == 'function') {
this.switchStoreAfter();
this.setNavigationBarTitleText();
}
},
deep: true
},
themeConfig:{
handler(nval,oval){
if(nval && oval && oval.color && nval.color != oval.color) {
this.loadThemeColor();
}
},
deep:true
}
},
methods: {
/**
* 设置页面标题
*/
setNavigationBarTitleText() {
let pages = getCurrentPages();
let currentPage = pages[pages.length - 1];
if (currentPage && currentPage.$holder && currentPage.$holder.navigationBarTitleText) {
let title = currentPage.$holder.navigationBarTitleText;
if (this.globalStoreInfo) title += '-' + this.globalStoreInfo.store_name;
if (title != currentPage.$holder.navigationBarTitleText) uni.setNavigationBarTitle({
title: title
})
} else {
setTimeout(() => {
this.setNavigationBarTitleText();
}, 800)
}
},
// 触发左侧菜单按键回调
menuTriggerKeyCodeCallBack(code) {
if (this.menuKeyCode.indexOf(code) != -1) {
let data = null;
for (let i = 0; i < this.menu.length; i++) {
let item = this.menu[i];
if (item.keyCode == code) {
data = item;
break;
}
}
if (data) {
// #ifdef H5
if (data.path == this.$route.path) return;
// #endif
// #ifdef APP-PLUS
if (data.path == '/' + this.$mp.page.route) return;
// #endif
this.$util.redirectTo(data.path, data.query ?? {});
}
}
},
themeColorSet() {
let theme = this.themeConfig;
this.themeColor = `--primary-color:${theme.color};`;
for (let i = 9; i >= 1; i--) {
let color = this.$util.colourBlend(theme.color, '#ffffff', (i / 10));
this.themeColor += `--primary-color-light-${i}:${color};`;
}
},
loadThemeColor(){
let time = setInterval(() => {
let theme = this.themeConfig;
if (theme && theme.color) {
this.themeColorSet();
clearInterval(time);
}
}, 50);
}
},
filters: {
/**
* 金额格式化
* @param {Object} money
*/
moneyFormat(money) {
if (isNaN(money)) return money;
return parseFloat(money).toFixed(2);
},
/**
* 时间格式化
* @param {Object} time 时间戳
* @param {Object} format 输出格式
*/
timeFormat(time, format = 'Y-m-d H:i:s') {
var date = new Date();
date.setTime(time * 1000);
var y = date.getFullYear();
var m = date.getMonth() + 1;
var d = date.getDate();
var h = date.getHours();
var i = date.getMinutes();
var s = date.getSeconds();
format = format.replace('Y', y);
format = format.replace('m', (m < 10 ? '0' + m : m));
format = format.replace('d', (d < 10 ? '0' + d : d));
format = format.replace('H', (h < 10 ? '0' + h : h));
format = format.replace('i', (i < 10 ? '0' + i : i));
format = format.replace('s', (s < 10 ? '0' + s : s));
return format;
},
mobileFormat(mobile) {
return mobile.substring(0, 4 - 1) + '****' + mobile.substring(6 + 1);
}
}
}

View File

@@ -0,0 +1,14 @@
// import { ipcRenderer } from 'electron';
export default {
send(call, param) {
if (window.ipcRenderer != undefined) {
window.ipcRenderer.send(call, param);
}
if (window.POS_ != undefined) {
window.POS_.send(call, param);
}
//window.ipcRenderer.send('Print', '渲染进程')
//window.ipcRenderer.invoke('Print', '渲染进程')
},
}

View File

@@ -0,0 +1,51 @@
import Config from './config.js'
const prefix = Config.baseUrl.replace(/(http:\/\/)|(https:\/\/)/g, '');
var oldPrefix = uni.getStorageSync('prefix');
// 域名不一致,清空
if (oldPrefix != prefix) {
uni.clearStorageSync();
}
uni.setStorageSync('prefix', prefix);
const handleKey = (key) => {
const storageKey = (prefix ? `${prefix}_` : '') + key;
return storageKey;
};
export function uniStorage() {
const setStorageSync = uni.setStorageSync;
const setStorage = uni.setStorage;
const getStorage = uni.getStorage;
const getStorageSync = uni.getStorageSync;
const removeStorage = uni.removeStorage;
const removeStorageSync = uni.removeStorageSync;
uni.setStorage = (options) => {
options.key = handleKey(options.key);
setStorage(options)
};
uni.setStorageSync = (key, data) => {
setStorageSync(handleKey(key), data)
};
uni.getStorage = (options) => {
options.key = handleKey(options.key);
getStorage(options)
};
uni.getStorageSync = (key) => {
return getStorageSync(handleKey(key))
};
uni.removeStorage = (options) => {
options.key = handleKey(options.key);
removeStorage(options)
};
uni.removeStorageSync = (key) => {
return removeStorageSync(handleKey(key))
}
}

View File

@@ -0,0 +1,340 @@
import Config from './config.js'
import Store from '@/store/index.js'
export default {
/**
* 页面跳转
* @param {string} to 跳转链接 /pages/idnex/index
* @param {Object} param 参数 {key : value, ...}
* @param {string} mode 模式
*/
redirectTo(to, param, mode) {
let tabbar = [
"/pages/billing/index", // 开单
"/pages/reserve/index", // 预约
"/pages/buycard/index", // 售卡
"/pages/recharge/index", // 充值
"/pages/verify/index" // 核销
];
if (tabbar.indexOf(to) != -1) mode = 'tabbar';
Store.commit('app/setCurrRoute', to);
let url = to;
if (param != undefined) {
Object.keys(param).forEach(function (key) {
if (url.indexOf('?') != -1) {
url += "&" + key + "=" + param[key];
} else {
url += "?" + key + "=" + param[key];
}
});
}
switch (mode) {
case 'tabbar':
// 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。
uni.switchTab({
url
});
break;
case 'redirectTo':
// 关闭当前页面,跳转到应用内的某个页面。
uni.redirectTo({
url
});
break;
case 'reLaunch':
// 关闭所有页面,打开到应用内的某个页面。
uni.reLaunch({
url
});
break;
default:
// 保留当前页面,跳转到应用内的某个页面
uni.navigateTo({
url
});
}
},
/**
* 图片路径转换
* @param {String} img_path 图片地址
* @param {Object} params 参数针对商品、相册里面的图片区分大中小size: big、mid、small
*/
img(img_path, params) {
var path = "";
if (img_path != undefined && img_path != "") {
if (img_path.split(',').length > 1) {
img_path = img_path.split(',')[0];
}
if (params && img_path) {
// 过滤默认图
let arr = img_path.split(".");
let suffix = arr[arr.length - 1];
arr.pop();
arr[arr.length - 1] = arr[arr.length - 1] + "_" + params.size.toUpperCase();
arr.push(suffix);
img_path = arr.join(".");
}
if (img_path.indexOf("http://") == -1 && img_path.indexOf("https://") == -1) {
path = Config.imgDomain + "/" + img_path;
} else {
path = img_path;
}
}
return path;
},
/**
* 验证当前数组或对象是否为空
* @param param 校验对象
*/
checkIsNotNull(param) {
if (param) {
if (typeof (param) == 'object') {
if (Array.isArray(param)) {
if (param.length > 0) return true
} else {
if (JSON.stringify(param) != '{}') return true
}
} else {
return true;
}
}
return false;
},
/**
* 金额格式化
* @param {Object} money
*/
moneyFormat(money) {
if (isNaN(parseFloat(money))) return money;
return parseFloat(money).toFixed(2);
},
/**
* 时间格式化
* @param {Object} time 时间戳
* @param {Object} format 输出格式
*/
timeFormat(time, format = 'y-m-d h:i:s') {
//一律用小写转换
format = format.toLowerCase();
if(time == 0) return '--';
var date = new Date();
date.setTime(time * 1000);
var y = date.getFullYear();
var m = date.getMonth() + 1;
var d = date.getDate();
var h = date.getHours();
var i = date.getMinutes();
var s = date.getSeconds();
format = format.replace('y', y);
format = format.replace('m', (m < 10 ? '0' + m : m));
format = format.replace('d', (d < 10 ? '0' + d : d));
format = format.replace('h', (h < 10 ? '0' + h : h));
format = format.replace('i', (i < 10 ? '0' + i : i));
format = format.replace('s', (s < 10 ? '0' + s : s));
return format;
},
/**
* 日期格式转时间戳
* @param {Object} string
*/
timeTurnTimeStamp(string) {
var f = string.split(' ', 2);
var d = (f[0] ? f[0] : '').split('-', 3);
var t = (f[1] ? f[1] : '').split(':', 3);
return (new Date(
parseInt(d[0], 10) || null,
(parseInt(d[1], 10) || 1) - 1,
parseInt(d[2], 10) || null,
parseInt(t[0], 10) || null,
parseInt(t[1], 10) || null,
parseInt(t[2], 10) || null
)).getTime() / 1000;
},
/**
* 获取当前页面路由
*/
getCurrRoute() {
let routes = getCurrentPages(); // 获取当前打开过的页面路由数组
return routes.length ? routes[routes.length - 1].route : '';
},
/**
* 显示消息提示框
* @param {Object} params 参数
*/
showToast(params = {}) {
params.title = params.title || "";
params.icon = params.icon || "none";
// params.position = params.position || 'bottom';
params.duration = params.duration || 1500;
uni.showToast(params);
if (params.success) params.success();
},
/*
* 深度拷贝对象
* @param {Object} obj
*/
deepClone(obj) {
const isObject = function (obj) {
return typeof obj == 'object';
}
if (!isObject(obj)) {
throw new Error('obj 不是一个对象!')
}
//判断传进来的是对象还是数组
let isArray = Array.isArray(obj)
let cloneObj = isArray ? [] : {}
//通过for...in来拷贝
for (let key in obj) {
cloneObj[key] = isObject(obj[key]) ? this.deepClone(obj[key]) : obj[key]
}
return cloneObj
},
/**
* 图片选择加上传
* @param num
* @param params
* @param callback
* @param url
*/
upload: function (num, params, callback, url) {
const app_type = 'pc';
const app_type_name = 'PC';
var data = {
token: uni.getStorageSync('cashierToken'),
app_type: app_type,
app_type_name: app_type_name
}
data = Object.assign(data, params);
var imgs_num = num;
var _self = this;
uni.chooseImage({
count: imgs_num,
sizeType: ['compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], //从相册或者拍照
success: async function (res) {
const tempFilePaths = res.tempFilePaths;
var _data = data;
var imgs = [];
for (var i = 0; i < tempFilePaths.length; i++) {
var path = await _self.upload_file_server(tempFilePaths[i], _data, params.path, url);
imgs.push(path);
}
typeof callback == 'function' && callback(imgs);
},
fail: err => {
console.log('图片上传错误', err)
}
});
},
//上传
upload_file_server(tempFilePath, data, path, url = "") {
if (url) {
var uploadUrl = Config.baseUrl + url
} else {
var uploadUrl = Config.baseUrl + '/cashier/storeapi/upload/' + path
}
data.site_id = uni.getStorageSync('siteId');
return new Promise((resolve, reject) => {
uni.uploadFile({
url: uploadUrl,
filePath: tempFilePath,
name: 'file',
formData: data,
success: function (res) {
var path_str = JSON.parse(res.data);
if (path_str.code >= 0) {
resolve(path_str.data.pic_path);
} else {
reject("error");
}
},
});
});
},
/**
* 验证手机号
* @param {string} mobile 被验证的mobile
* @return {object} 验证后的结果
**/
verifyMobile(mobile) {
var parse =/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/.test(mobile);
return parse;
},
clearStoreData() {
Store.commit('app/setGlobalStoreInfo', null);
Store.commit('app/setGlobalStoreId', 0);
Store.commit('app/setGlobalMemberInfo', null);
Store.commit('app/setUserInfo', null);
Store.commit('app/setMenu', []);
// 开单
Store.commit('billing/setGoodsData', {});
Store.commit('billing/setPendOrderId',0);
Store.commit('billing/setGoodsIds', []);
Store.commit('billing/setOrderData', {});
Store.commit('billing/setActive', '');
Store.commit('billing/setIsScanTrigger', false);
// 售卡
Store.commit('buycard/setGoodsData', {});
Store.commit('buycard/setOrderData', {});
Store.commit('buycard/setActive', '');
// 充值
Store.commit('recharge/setActive', '');
},
/**
* 颜色减值
* @param {Object} c1
* @param {Object} c2
* @param {Object} ratio
*/
colourBlend(c1, c2, ratio) {
ratio = Math.max(Math.min(Number(ratio), 1), 0)
let r1 = parseInt(c1.substring(1, 3), 16)
let g1 = parseInt(c1.substring(3, 5), 16)
let b1 = parseInt(c1.substring(5, 7), 16)
let r2 = parseInt(c2.substring(1, 3), 16)
let g2 = parseInt(c2.substring(3, 5), 16)
let b2 = parseInt(c2.substring(5, 7), 16)
let r = Math.round(r1 * (1 - ratio) + r2 * ratio)
let g = Math.round(g1 * (1 - ratio) + g2 * ratio)
let b = Math.round(b1 * (1 - ratio) + b2 * ratio)
r = ('0' + (r || 0).toString(16)).slice(-2)
g = ('0' + (g || 0).toString(16)).slice(-2)
b = ('0' + (b || 0).toString(16)).slice(-2)
return '#' + r + g + b
},
//商品类型字典
goodsClassDict:{
real:1,
virtual:2,
virtualcard:3,
service:4,
card:5,
weigh:6,
},
setLocalConfig(obj){
var local_config = this.getLocalConfig();
local_config = Object.assign(local_config, obj);
uni.setStorageSync('local_config', local_config);
},
getLocalConfig(){
var local_config = uni.getStorageSync('local_config');
if(!local_config) local_config = {};
local_config = Object.assign({
printerSelectType:'all',
printerSelectIds:[],
}, local_config);
return local_config;
},
}

View File

@@ -0,0 +1,173 @@
/**
* 数据验证(表单验证)
*/
module.exports = {
error: '',
check: function(data, rule) {
for (var i = 0; i < rule.length; i++) {
if (!rule[i].checkType) {
return true;
}
if (!rule[i].name) {
return true;
}
if (!rule[i].errorMsg) {
return true;
}
if (!data[rule[i].name]) {
this.error = rule[i].errorMsg;
return false;
}
switch (rule[i].checkType) {
case 'custom':
if (typeof rule[i].validate == 'function') {
if (!rule[i].validate(data[rule[i].name])) {
this.error = rule[i].errorMsg;
return false;
}
}
break;
case 'required':
var reg = new RegExp('/[\S]+/');
if (reg.test(data[rule[i].name])) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'string':
var reg = new RegExp('^.{' + rule[i].checkRule + '}$');
if (!reg.test(data[rule[i].name])) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'int':
var reg = new RegExp('^(-[1-9]|[1-9])[0-9]{' + rule[i].checkRule + '}$');
if (!reg.test(data[rule[i].name])) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'between':
if (!this.isNumber(data[rule[i].name])) {
this.error = rule[i].errorMsg;
return false;
}
var minMax = rule[i].checkRule.split(',');
minMax[0] = Number(minMax[0]);
minMax[1] = Number(minMax[1]);
if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'betweenD':
var reg = /^-?[1-9][0-9]?$/;
if (!reg.test(data[rule[i].name])) {
this.error = rule[i].errorMsg;
return false;
}
var minMax = rule[i].checkRule.split(',');
minMax[0] = Number(minMax[0]);
minMax[1] = Number(minMax[1]);
if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'betweenF':
var reg = /^-?[0-9][0-9]?.+[0-9]+$/;
if (!reg.test(data[rule[i].name])) {
this.error = rule[i].errorMsg;
return false;
}
var minMax = rule[i].checkRule.split(',');
minMax[0] = Number(minMax[0]);
minMax[1] = Number(minMax[1]);
if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'same':
if (data[rule[i].name] != rule[i].checkRule) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'notsame':
if (data[rule[i].name] == rule[i].checkRule) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'email':
var reg = /^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
if (!reg.test(data[rule[i].name])) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'phoneno':
var reg = /^\d{11}$/;
if (!reg.test(data[rule[i].name])) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'zipcode':
var reg = /^[0-9]{6}$/;
if (!reg.test(data[rule[i].name])) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'reg':
var reg = new RegExp(rule[i].checkRule);
if (!reg.test(data[rule[i].name])) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'in':
if (rule[i].checkRule.indexOf(data[rule[i].name]) == -1) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'notnull':
if (data[rule[i].name] == 0 || data[rule[i].name] == undefined || data[rule[i].name] ==
null || data[rule[i].name]
.length < 1) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'lengthMin':
if (data[rule[i].name].length < rule[i].checkRule) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'lengthMax':
if (data[rule[i].name].length > rule[i].checkRule) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'numberId':
var pattern = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
if (!pattern.test(data[rule[i].name])) {
this.error = rule[i].errorMsg;
return false
}
break;
}
}
return true;
},
isNumber: function(checkVal) {
var reg = /^-?[1-9][0-9]?.?[0-9]*$/;
return reg.test(checkVal);
}
}

View File

@@ -0,0 +1,243 @@
export default [{
title: '开单',
icon: 'iconkaidan',
path: '/pages/billing/index',
name: 'billing',
keyCode: 'F6' // 触发按键
},
{
title: '售卡',
icon: 'iconqia',
path: '/pages/buycard/index',
name: 'buycard',
keyCode: 'F7' // 触发按键
},
{
title: '充值',
icon: 'iconchongzhidingdan',
path: '/pages/recharge/index',
name: 'recharge',
keyCode: 'F8' // 触发按键
},
{
title: '订单',
icon: 'icondingdan',
path: '/pages/order/orderlist',
name: 'order_list',
keyCode: 'F9' // 触发按键
},
{
title: '会员',
icon: 'iconkehuguanli',
path: '/pages/member/list',
name: 'member_list',
keyCode: 'F10' // 触发按键
},
{
title: '核销',
icon: 'iconhexiao',
path: '/pages/verify/index',
name: 'verify_index',
keyCode: 'F11' // 触发按键
},
{
title: '更多',
icon: 'iconicon_yingyongguanli',
childshow: true,
children: [{
title: '收银',
children: [{
title: '开单',
icon: 'iconkaidan',
path: '/pages/billing/index',
name: 'billing'
},
{
title: '售卡',
icon: 'iconqia',
path: '/pages/buycard/index',
name: 'buycard'
},
{
title: '核销',
icon: 'iconhexiao',
path: '/pages/verify/index',
name: 'verify_index'
},
{
title: '预约',
icon: 'iconyuyueguanli',
path: '/pages/reserve/index',
name: 'reserve_index',
addon: 'cardservice'
},
{
title: '充值',
icon: 'iconchongzhidingdan',
path: '/pages/recharge/index',
name: 'recharge'
},
{
title: '交班',
icon: 'icon12jiaobanbiao',
path: '/pages/index/change_shifts',
}
]
},
{
title: '管理',
children: [{
title: '商品管理',
icon: 'iconshangpinguanli',
path: '/pages/goods/goodslist',
name: 'goods_list'
},
{
title: '会员管理',
icon: 'iconkehuguanli',
path: '/pages/member/list',
name: 'member_list'
},
{
title: '员工管理',
icon: 'iconjishi',
path: '/pages/user/list',
name: 'user_list'
},
{
title: '订单管理',
icon: 'icondingdan',
path: '/pages/order/orderlist',
name: 'order_list'
},
{
title: '退款维权',
icon: 'iconjishi',
path: '/pages/order/orderrefund',
name: 'order_refund_list'
},
{
title: '交班记录',
icon: 'icon12jiaobanbiao',
path: '/pages/index/change_shiftsrecord',
name: 'change_shifts_record_list'
}
]
},
{
title: '营销',
name : 'promotion',
children:[{
title:'优惠券',
icon:'icon31hongbao',
path:'/pages/marketing/coupon_list',
name:'coupon_list'
}]
},
{
title: '库存',
name: 'stock',
children: [{
title: '出库单',
icon: 'iconchukudan',
path: '/pages/stock/wastage',
name: 'stock_wastage',
addon: 'stock'
},
{
title: '入库单',
icon: 'iconrukudan',
path: '/pages/stock/storage',
name: 'stock_storage',
addon: 'stock'
},
{
title: '调拨单',
icon: 'icontiaobodan',
path: '/pages/stock/allocate',
name: 'stock_allocate',
addon: 'stock'
},
{
title: '库存盘点',
icon: 'iconkucunpandian',
path: '/pages/stock/check',
name: 'stock_check',
addon: 'stock'
},
{
title: '库存管理',
icon: 'iconkucunguanli',
path: '/pages/stock/manage',
name: 'stock_manage',
addon: 'stock'
}
]
},
{
title: '数据',
children: [{
title: '门店结算',
icon: 'iconshourujiesuan',
path: '/pages/store/settlement',
addon: 'store',
name: 'store_settlement'
},
{
title: '营业数据',
icon: 'iconyingyeshujuguanliputong',
path: '/pages/stat/index',
}
]
},
{
title: '设置',
children: [{
title: '门店设置',
icon: 'icongongyingshang',
path: '/pages/store/index',
name: 'store_config_root',
},
{
title: '收款设置',
icon: 'iconshoukuan-',
path: '/pages/collectmoney/config',
// name: 'collectmoney_config',
},
{
title: '预约设置',
icon: 'iconyuyue',
path: '/pages/reserve/config',
name: 'reserve_config',
addon: 'cardservice'
},
{
title: '小票打印',
icon: 'icondayin',
name: 'printer_config',
path: '/pages/printer/list',
},
{
title: '配送员',
icon: 'iconpeisong',
path: '/pages/store/deliver',
name: 'store_deliver_config'
},
{
title: '电子秤管理',
icon: 'icondianzicheng',
name: 'heavt_config',
path: '/pages/scale/list',
addon: 'scale'
},
{
title: '本机设置',
icon: 'iconbenji',
path: '/pages/local/config',
name: 'local_config',
},
]
}
]
}
]

View File

@@ -0,0 +1,48 @@
<template>
<unipopup ref="couponCategoryPop" type="center">
<view class="coupon-category-pop">
<view class="header flex justify-between">
<view class="title">选择分类</view>
<!-- <view class="pop-header-close" @click="$refs.couponCategoryPop.close()">
<text class="iconguanbi1 iconfont"></text>
</view> -->
</view>
<!-- :checkStrictly="true" -->
<view :overflow-y="true" class="body">
<scroll-view class="tree" :overflow-y="true">
<DaTreeVue2
ref="DaTreeRef"
:data="treeData"
labelField="category_name"
valueField="category_id"
childrenField="child_list"
:themeColors="'var(--primary-color)'"
expandChecked
showCheckbox
:defaultCheckedKeys="defaultCheckedKeysValue"
@change="handleTreeChange"></DaTreeVue2>
</scroll-view>
</view>
<view class="footer flex justify-end">
<button type="default" class="confirm btn" @click="confirm">确认</button>
<button type="default" class="btn" @click="$refs.couponCategoryPop.close()">取消</button>
</view>
</view>
</unipopup>
</template>
<script>
import DaTreeVue2 from '@/components/da-tree-vue2/index.vue'
import unipopup from '@/components/uni-popup/uni-popup.vue';
import index from './index.js';
export default {
components: {
DaTreeVue2,
unipopup,
},
mixins: [index]
};
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

View File

@@ -0,0 +1,87 @@
import {
getGoodsCategory
} from '@/api/goods.js'
export default {
name: 'couponCategoryPopup',
data() {
return {
treeData: [],
defaultCheckedKeysValue: [],
checkList: [], //选中以及半选中数据
}
},
mounted() {
this.getGoodsCategoryFn()
},
methods: {
getGoodsCategoryFn() {
getGoodsCategory({
level: 3
}).then(res => {
this.treeData = res.data
})
},
open(value) {
this.defaultCheckedKeysValue = this.$util.deepClone(value)
this.$refs.couponCategoryPop.open()
},
handleTreeChange(val) {
this.defaultCheckedKeysValue = this.$util.deepClone(val)
let halfCheckList = this.$refs.DaTreeRef.getHalfCheckedKeys()||[]
this.checkList = this.$util.deepClone(val.concat(halfCheckList))
},
//处理数据
getSelectedIdsAndNames(tree_selected, tree_all) {
let name_arr = [];
let id_arr = [];
let selected_num = 0;
for (let i in tree_selected) {
let item_selected = tree_selected[i];
let item_all = null;
tree_all.forEach((item) => {
if (item.category_id === item_selected.category_id) {
item_all = item;
return;
}
})
if (!item_all) throw '对比数据有误';
let title = item_selected.category_name;
id_arr.push(item_selected.category_id);
if (item_selected.child_num > 0) {
let res = this.getSelectedIdsAndNames(item_selected.child_list, item_all.child_list);
if (res.selected_num == item_all.child_num) {
selected_num++;
} else {
title += '' + res.name_arr.join('、') + '';
}
id_arr = id_arr.concat(res.id_arr);
} else {
selected_num++;
}
name_arr.push(title);
}
return {
selected_num: selected_num,
name_arr: name_arr,
id_arr: id_arr,
};
},
confirm() {
if(!this.checkList.length){
this.$util.showToast({
title: "请选择商品分类"
});
return false
}
getGoodsCategory({
level: 3,
category_ids:this.checkList.join(',')
}).then(res => {
let selectedData = this.getSelectedIdsAndNames(res.data, this.treeData);
this.$emit('confirm',selectedData)
this.$refs.couponCategoryPop.close()
})
}
}
}

View File

@@ -0,0 +1,42 @@
.coupon-category-pop{
width: 7rem;
background-color: #fff;
border-radius: 0.06rem;
.header{
padding: 0.15rem 0.2rem;
font-size: 0.14rem;
border-bottom: 0.01rem solid #e6e6e6;
}
.body{
padding: 0.2rem 0.3rem;
padding-bottom: 0.15rem;
box-sizing: border-box;
height: 4rem;
.tree{
height: 100%;
}
}
.footer{
padding: 0 0.3rem;
padding-bottom: 0.15rem;
.btn {
margin: 0;
display: inline-block;
padding: 0 0.2rem;
height: 0.36rem;
line-height: .36rem;
font-size: 0.14rem;
border-radius: 3px;
&.confirm{
background-color: var(--primary-color);
color: #fff;
margin-right: 0.15rem;
&::after{
border-width: 0;
}
}
}
}
}

View File

@@ -0,0 +1,110 @@
# 1.4.1
## 版本调整
建议更新,但需要注意,异步数据的时候,后台需返回 leaf 字段来判断是否末项数据
1. **调整数据项格式,新增 `leaf` 字段,来判断是否为末节点**
2. **调整数据项格式,新增 `sort` 字段,来排序节点位置**
3. **注意:异步加载数据,当为末项的时候,需要服务端数据返回 `leaf` 字段**
4. 新增 `alwaysFirstLoad` ,即异步数据总会在第一次展开节点时,拉取一次后台数据,来比对是否一致
5. 拆分 `field` 属性,**注意: 1.5.0 版本后将移除 `field` 属性**
6. 新增 `labelField``field.label`,指定节点对象中某个属性为**标签**字段,默认`label`
7. 新增 `valueField``field.key`,指定节点对象中某个属性为**值**字段,默认`value`
8. 新增 `childrenField``field.children`,指定节点对象中某个属性为**子树节点**字段,默认`children`
9. 新增 `disabledField``field.disabled`,指定节点对象中某个属性为**禁用**字段,默认`disabled`
10. 新增 `appendField``field.append`,指定节点对象中某个属性为**副标签**字段,默认`append`
11. 新增 `leafField``field.label`,指定节点对象中某个属性为**末级节点**字段,默认`leaf`
12. 新增 `sortField``field.label`,指定节点对象中某个属性为**排序**字段,默认`sort`
13. 新增 `isLeafFn` ,用来自定义控制数据项的末项
14. 更多的项目示例
15. 支持单选取消选中
16. 修复节点展开时可能存在的 bug
17. 修复节点选择可能存在的 bug
18. 调整为子节点默认继承父节点禁用属性
19. `setExpandedKeys` 添加参数一为 `all` 即可支持一键展开/收起全部节点
20. 其它更多优化
# 1.3.4
优化
1. 优化图标字体命名
# 1.3.3
版本同步于 Vue3 版,[查看 Vue3 版更新日志](https://ext.dcloud.net.cn/plugin?id=12384&update_log)
# 1.3.2
版本同步于 Vue3 版,[查看 Vue3 版更新日志](https://ext.dcloud.net.cn/plugin?id=12384&update_log)
# 1.3.1.1
修复
1. 修复 APP 兼容性引起的报错
# 1.3.1
## 建议更新
### 1.2.2~1.3.1 更新预览
1. 新增支持主题换色
2. 新增支持点击标签也能选中节点
3. 新增`field`字段 `append` 用于在标签后面显示小提示
4. 方法`setExpandedKeys`支持加载动态数据
5. 支持单选的`onlyRadioLeaf``true`时可点父节点展开/收起
6. 新增 `expandChecked`,控制选择时是否展开当前已选的所有下级节点
7. 新增 `checkedDisabled`,支持渲染禁用值
8. 新增 `packDisabledkey`,支持返回已选中的禁用的 key
9. 更多细节修复、优化请移步 Vue3 版的更新日志
后续版本仍不会实时同步 Vue3 版本,如急需新功能,请移步 Vue3 版
版本同步于 Vue3 版,[查看 Vue3 版更新日志](https://ext.dcloud.net.cn/plugin?id=12384&update_log)
# 1.2.2
## 建议更新,优化诸多问题
版本同步于 Vue3 版,[查看 Vue3 版更新日志](https://ext.dcloud.net.cn/plugin?id=12384&update_log)
# 1.2.1
版本同步于 Vue3 版,[查看 Vue3 版更新日志](https://ext.dcloud.net.cn/plugin?id=12384&update_log)
# 1.2.0.1
优化
1. 优化小程序兼容
# 1.2.0
版本同步于 Vue3 版,[查看 Vue3 版更新日志](https://ext.dcloud.net.cn/plugin?id=12384&update_log)
# 1.1.1.1
修复
1. 修复同步版本的错误写法引起的报错
# 1.1.1
版本同步于 Vue3 版,[查看 Vue3 版更新日志](https://ext.dcloud.net.cn/plugin?id=12384&update_log)
# 1.1.0
版本同步于 Vue3 版,[查看 Vue3 版更新日志](https://ext.dcloud.net.cn/plugin?id=12384&update_log)
# 1.0.6
新增
版本同步于 Vue3 版,[查看 Vue3 版更新日志](https://ext.dcloud.net.cn/plugin?id=12384&update_log)
# 1.0.5
版本同步于 Vue3 版,[查看 Vue3 版更新日志](https://ext.dcloud.net.cn/plugin?id=12384),基于 Vue2 进行开发,支持单选、多选,全平台兼容。

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,183 @@
// @ts-nocheck
export default {
/**
* 树的数据
*/
data: {
type: Array,
default: () => [],
},
/**
* 主题色
*/
themeColors: {
type: String,
default: '#007aff',
},
/**
* 是否开启多选,默认单选
*/
showCheckbox: {
type: Boolean,
default: false,
},
/**
* 默认选中的节点注意单选时为单个key多选时为key的数组
*/
defaultCheckedKeys: {
type: [Array, String, Number],
default: null,
},
/**
* 选择框的位置,可选 left/right
*/
checkboxPlacement: {
type: String,
default: 'left',
},
/**
* 是否默认展开全部
*/
defaultExpandAll: {
type: Boolean,
default: false,
},
/**
* 默认展开的节点
*/
defaultExpandedKeys: {
type: Array,
default: null,
},
/**
* 是否自动展开到选中的节点,默认不展开
*/
expandChecked: {
type: Boolean,
default: false,
},
/**
* 子项缩进距离默认40单位rpx
*/
indent: {
type: Number,
default: 0.4,
},
/**
* (旧)字段对应内容,默认为 {label: 'label',key: 'key', children: 'children', disabled: 'disabled', append: 'append'}
* 注意1.5.0版本后不再兼容
*/
field: {
type: Object,
default: null,
},
/**
* 标签字段(新,拆分了)
*/
labelField: {
type: String,
default: 'label',
},
/**
* 值字段(新,拆分了)
*/
valueField: {
type: String,
default: 'value',
},
/**
* 下级字段(新,拆分了)
*/
childrenField: {
type: String,
default: 'children',
},
/**
* 禁用字段(新,拆分了)
*/
disabledField: {
type: String,
default: 'disabled',
},
/**
* 末级节点字段(新,拆分了)
*/
leafField: {
type: String,
default: 'leaf',
},
/**
* 副标签字段(新,拆分了)
*/
appendField: {
type: String,
default: 'append',
},
/**
* 排序字段(新,拆分了)
*/
sortField: {
type: String,
default: 'sort',
},
isLeafFn: {
type: Function,
default: null,
},
/**
* 是否显示单选图标,默认显示
*/
showRadioIcon: {
type: Boolean,
default: true,
},
/**
* 单选时只允许选中末级,默认可随意选中
*/
onlyRadioLeaf: {
type: Boolean,
default: false,
},
/**
* 多选时,是否执行父子不关联的任意勾选,默认父子关联
*/
checkStrictly: {
type: Boolean,
default: false,
},
/**
* 为 true 时,空的 children 数组会显示展开图标
*/
loadMode: {
type: Boolean,
default: false,
},
/**
* 异步加载接口
*/
loadApi: {
type: Function,
default: null,
},
/**
* 是否总在首次的时候加载一下内容,来比对是否一致
*/
alwaysFirstLoad: {
type: Boolean,
default: false,
},
/**
* 是否渲染(操作)禁用值
*/
checkedDisabled: {
type: Boolean,
default: false,
},
/**
* 是否返回已禁用的但已选中的key
*/
packDisabledkey: {
type: Boolean,
default: true,
},
}

View File

@@ -0,0 +1,303 @@
# da-tree-vue2
一个基于 Vue2 的 tree(树)组件,同时支持主题换色,可能是最适合你的 tree(树)组件
`内容同步于 Vue3 版本,在此查看 ===>` **[Vue3 版](https://ext.dcloud.net.cn/plugin?id=12384)**
_与 Vue3 版本版本不同的是,此版本兼容更全面,比如 360 小程序、快应用等均支持_
### 关于使用
可在右侧的`使用 HBuilderX 导入插件``下载示例项目ZIP`,方便快速上手。
可通过下方的示例及文档说明,进一步了解使用组件相关细节参数。
插件地址https://ext.dcloud.net.cn/plugin?id=12692
### 组件示例
```jsx
<template>
<view>
<view>多选</view>
<view><button @click="doCheckedTree(['2'],true)">全选</button></view>
<view><button @click="doCheckedTree(['2'],false)">取消全选</button></view>
<view><button @click="doCheckedTree(['211','222'],true)">选中指定节点</button></view>
<view><button @click="doCheckedTree(['211','222'],false)">取消选中指定节点</button></view>
<view><button @click="doExpandTree('all',true)">展开全部节点</button></view>
<view><button @click="doExpandTree('all',false)">收起全部节点</button></view>
<view><button @click="doExpandTree(['22','23'],true)">展开节点</button></view>
<view><button @click="doExpandTree(['22','23'],false)">收起节点</button></view>
<DaTreeVue2
ref="DaTreeRef"
:data="roomTreeData"
labelField="name"
valueField="id"
defaultExpandAll
showCheckbox
:defaultCheckedKeys="defaultCheckedKeysValue"
@change="handleTreeChange"
@expand="handleExpandChange" />
<view>单选</view>
<DaTreeVue2
:data="roomTreeData"
labelField="name"
valueField="id"
defaultExpandAll
:defaultCheckedKeys="defaultCheckedKeysValue2"
@change="handleTreeChange"
@expand="handleExpandChange" />
<view>默认展开指定节点</view>
<DaTreeVue2
:data="roomTreeData"
labelField="name"
valueField="id"
showCheckbox
:defaultExpandedKeys="defaultExpandKeysValue3"
@change="handleTreeChange"
@expand="handleExpandChange" />
<view>异步加载数据</view>
<DaTreeVue2
:data="roomTreeData"
labelField="name"
valueField="id"
showCheckbox
loadMode
:loadApi="GetApiData"
defaultExpandAll
@change="handleTreeChange"
@expand="handleExpandChange" />
</view>
</template>
```
```js
/**
* 模拟创建一个接口数据
*/
function GetApiData(currentNode) {
const { key } = currentNode
return new Promise((resolve) => {
setTimeout(() => {
// 模拟返回空数据
if (key.indexOf('-') > -1) {
return resolve(null)
// return resolve([])
}
return resolve([
{
id: `${key}-1`,
name: `行政部X${key}-1`,
},
{
id: `${key}-2`,
name: `财务部X${key}-2`,
append: '定义了末项数据',
leaf: true,
},
{
id: `${key}-3`,
name: `资源部X${key}-3`,
},
{
id: `${key}-4`,
name: `资源部X${key}-3`,
append: '被禁用,无展开图标',
disabled: true,
},
])
}, 2000)
})
}
import DaTreeVue2 from '@/components/da-tree-vue2/index.vue'
export default {
components: { DaTreeVue2 },
data() {
return {
GetApiData,
// key的类型必须对应树数据key的类型
defaultCheckedKeysValue: ['211', '222'],
defaultCheckedKeysValue2: '222',
defaultExpandKeysValue3: ['212', '231'],
roomTreeData: [
{
id: '2',
name: '行政中心',
children: [
{
id: '21',
name: '行政部',
children: [
{
id: '211',
name: '行政一部',
children: null,
},
{
id: '212',
name: '行政二部',
children: [],
disabled: true,
},
],
},
{
id: '22',
name: '财务部',
children: [
{
id: '221',
name: '财务一部',
children: [],
disabled: true,
},
{
id: '222',
name: '财务二部',
children: [],
},
],
},
{
id: '23',
name: '人力资源部',
children: [
{
id: '231',
name: '人力一部',
children: [],
},
{
id: '232',
name: '人力二部',
append: '更多示例,请下载示例项目查看',
},
],
},
],
},
],
}
},
methods: {
doExpandTree(keys, expand) {
this.$refs.DaTreeRef?.setExpandedKeys(keys, expand)
const gek = this.$refs.DaTreeRef?.getExpandedKeys()
console.log('当前已展开的KEY ==>', gek)
},
doCheckedTree(keys, checked) {
this.$refs.DaTreeRef?.setCheckedKeys(keys, checked)
const gek = this.$refs.DaTreeRef?.getCheckedKeys()
console.log('当前已选中的KEY ==>', gek)
},
handleTreeChange(allSelectedKeys, currentItem) {
console.log('handleTreeChange ==>', allSelectedKeys, currentItem)
},
handleExpandChange(expand, currentItem) {
console.log('handleExpandChange ==>', expand, currentItem)
},
},
}
```
** 更多示例请下载/导入示例项目 ZIP 查看 **
### 组件参数
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| :------------------ | :------------------------------ | :--------- | :--- | :--------------------------------------------------------------------------- |
| data | `Array` | - | 是 | 树的数据 |
| themeColor | `String` | `#007aff` | 否 | 主题色,十六进制 |
| defaultCheckedKeys | `Array` \| `Number` \| `String` | - | 否 | 默认选中的节点,单选为单个 key多选为 key 的数组 |
| showCheckbox | `Boolean` | `false` | 否 | 是否开启多选,默认单选 |
| checkStrictly | `Boolean` | `false` | 否 | 多选时,是否执行父子不关联的任意勾选,默认父子关联 |
| showRadioIcon | `Boolean` | `true` | 否 | 是否显示单选图标,默认显示 |
| onlyRadioLeaf | `Boolean` | `true` | 否 | 单选时只允许选中末级,默认可随意选中 |
| defaultExpandAll | `Boolean` | `false` | 否 | 是否默认展开全部 |
| defaultExpandedKeys | `Array` | - | 否 | 默认展开的节点 |
| indent | `Number` | `40` | 否 | 子项缩进距离,单位 rpx |
| checkboxPlacement | `String` | `left` | 否 | 选择框的位置,可选 left/right |
| loadMode | `Boolean` | `false` | 否 | 为 true 时,空的 children 数组会显示展开图标 |
| loadApi | `Function` | - | 否 | 选择框的位置,可选 left/right |
| checkedDisabled | `Boolean` | `false` | 否 | 是否渲染禁用值,默认不渲染 |
| packDisabledkey | `Boolean` | `true` | 否 | 是否返回已禁用的但已选中的 key默认返回禁用已选值 |
| expandChecked | `Boolean` | `false` | 否 | 是否自动展开到选中的节点,默认不展开 |
| alwaysFirstLoad | `Boolean` | `false` | 否 | 是否总在首次的时候加载一下内容,默认不加载,否则只有展开末级节点才会加载数据 |
| isLeafFn | `Function` | - | 否 | 自定义函数返回来控制数据项的末项 |
| field | `Object` | - | 否 | 字段对应内容,格式参考下方(1.5.0 后移除,请用单独的字段匹配) |
| labelField | `String` | `label` | 否 | 指定节点对象中某个属性为标签字段,默认`label` |
| valueField | `String` | `value` | 否 | 指定节点对象中某个属性为值字段,默认`value` |
| childrenField | `String` | `children` | 否 | 指定节点对象中某个属性为子树节点字段,默认`children` |
| disabledField | `String` | `disabled` | 否 | 指定节点对象中某个属性为禁用字段,默认`disabled` |
| appendField | `String` | `append` | 否 | 指定节点对象中某个属性为副标签字段,默认`append` |
| leafField | `String` | `leaf` | 否 | 指定节点对象中某个属性为末级节点字段,默认`leaf` |
| sortField | `String` | `sort` | 否 | 指定节点对象中某个属性为排序字段,默认`sort` |
**field 格式(1.5.0 后移除,请用单独的字段匹配)**
```js
{
label: 'label',
key: 'key',
children: 'children',
disabled: 'disabled',
append: 'append'
}
```
### 组件事件
| 事件名称 | 回调参数 | 说明 |
| :------- | :-------------------------------------- | :-------------- |
| change | `(allCheckedKeys, currentItem) => void` | 选中时回调 |
| expand | `(expandState, currentItem) => void` | 展开/收起时回调 |
### 组件方法
| 方法名称 | 参数 | 说明 |
| :------------------ | :--------------- | :------------------------------------------------------------------------------------------------ |
| setCheckedKeys | `(keys,checked)` | 设置指定 key 的节点选中/取消选中的状态。注: keys 单选时为 key多选时为 key 的数组 |
| setExpandedKeys | `(keys,expand)` | 设置指定 key 的节点展开/收起的状态,当 keys 为 all 时即代表展开/收起全部。注keys 为数组或 `all` |
| getCheckedKeys | - | 返回已选的 key |
| getHalfCheckedKeys | - | 返回半选的 key |
| getUncheckedKeys | - | 返回未选的 key |
| getCheckedNodes | - | 返回已选的节点 |
| getUncheckedNodes | - | 返回未选的节点 |
| getHalfCheckedNodes | - | 返回半选的节点 |
| getExpandedKeys | - | 返回已展开的 key |
| getUnexpandedKeys | - | 返回未展开的 key |
| getExpandedNodes | - | 返回已展开的节点 |
| getUnexpandedNodes | - | 返回未展开的节点 |
### 组件版本
v1.4.1
### 差异化
已通过测试
> - H5 页面
> - 微信小程序
> - 支付宝、钉钉小程序
> - 字节跳动、抖音、今日头条小程序
> - 百度小程序
> - 飞书小程序
> - QQ 小程序
> - 京东小程序
> - 快应用
> - 360 小程序
未测试
> - 快手小程序由于非企业用户暂无演示
### 开发组
[@CRLANG](https://crlang.com)

View File

@@ -0,0 +1,151 @@
// @ts-nocheck
/** 未选 */
export const unCheckedStatus = 0
/** 半选 */
export const halfCheckedStatus = 1
/** 选中 */
export const isCheckedStatus = 2
/**
* 深拷贝内容
* @param originData 拷贝对象
* @author crlang(https://crlang.com)
*/
export function deepClone(originData) {
const type = Object.prototype.toString.call(originData)
let data
if (type === '[object Array]') {
data = []
for (let i = 0; i < originData.length; i++) {
data.push(deepClone(originData[i]))
}
} else if (type === '[object Object]') {
data = {}
for (const prop in originData) {
// eslint-disable-next-line no-prototype-builtins
if (originData.hasOwnProperty(prop)) { // 非继承属性
data[prop] = deepClone(originData[prop])
}
}
} else {
data = originData
}
return data
}
/**
* 获取所有指定的节点
* @param type
* @param value
* @author crlang(https://crlang.com)
*/
export function getAllNodes(list, type, value, packDisabledkey = true) {
if (!list || list.length === 0) {
return []
}
const res = []
for (let i = 0; i < list.length; i++) {
const item = list[i]
if (item[type] === value) {
if ((packDisabledkey && item.disabled) || !item.disabled) {
res.push(item)
}
}
}
return res
}
/**
* 获取所有指定的key值
* @param type
* @param value
* @author crlang(https://crlang.com)
*/
export function getAllNodeKeys(list, type, value, packDisabledkey = true) {
if (!list || list.length === 0) {
return null
}
const res = []
for (let i = 0; i < list.length; i++) {
const item = list[i]
if (item[type] === value) {
if ((packDisabledkey && item.disabled) || !item.disabled) {
res.push(item.key)
}
}
}
return res.length ? res : null
}
/**
* 错误输出
*
* @param msg
*/
export function logError(msg, ...args) {
console.error(`DaTree: ${msg}`, ...args)
}
const toString = Object.prototype.toString
export function is(val, type) {
return toString.call(val) === `[object ${type}]`
}
/**
* 是否对象(Object)
* @param val
*/
export function isObject(val) {
return val !== null && is(val, 'Object')
}
/**
* 是否数字(Number)
* @param val
*/
export function isNumber(val) {
return is(val, 'Number')
}
/**
* 是否字符串(String)
* @param val
*/
export function isString(val) {
return is(val, 'String')
}
/**
* 是否函数方法(Function)
* @param val
*/
export function isFunction(val) {
return typeof val === 'function'
}
/**
* 是否布尔(Boolean)
* @param val
*/
export function isBoolean(val) {
return is(val, 'Boolean')
}
/**
* 是否数组(Array)
* @param val
*/
export function isArray(val) {
return val && Array.isArray(val)
}

View File

@@ -0,0 +1,159 @@
import {getCardList} from '@/api/card.js';
import {mapGetters} from 'vuex';
export default {
name: 'nsCard',
props: {
type: {
type: String,
default: 'oncecard'
}
},
data() {
return {
goodsType: '',
pageSize: 35,
onceCardData: {
page: 0,
total: 1,
list: []
},
timeCardData: {
page: 0,
total: 1,
list: []
},
commonCardData: {
page: 0,
total: 1,
list: []
},
itemNum: 3,
mediaQueryOb: null,
selectCardSkuId: [],
isLoad: false
};
},
created() {
this.goodsType = this.type;
this.init();
},
computed: {
...mapGetters(['buyCardGoodsData'])
},
watch: {
buyCardGoodsData: {
// 每个属性值发生变化就会调用这个函数
handler(newVal, oldVal) {
this.selectCardSkuId = [];
if(!Object.values(this.buyCardGoodsData).length) return false;
Object.values(this.buyCardGoodsData).forEach((item,index)=>{
this.selectCardSkuId.push(item.sku_id);
});
},
// 深度监听 属性的变化
deep: true
}
},
mounted() {
this.mediaQueryOb = uni.createMediaQueryObserver(this);
this.mediaQueryOb.observe({maxWidth: 1500}, matches => {
if (matches) this.itemNum = 2;
});
this.mediaQueryOb.observe({minWidth: 1501, maxWidth: 1700}, matches => {
if (matches) this.itemNum = 3;
});
this.mediaQueryOb.observe({minWidth: 1701}, matches => {
if (matches) this.itemNum = 4;
});
},
destroyed() {
this.mediaQueryOb.disconnect();
},
methods: {
init() {
this.isLoad = false;
this.onceCardData.page = 0;
this.timeCardData.page = 0;
this.commonCardData.page = 0;
this.getOnceCard();
this.getTimeCard();
this.getCommonCard();
},
switchGoodsType(type) {
this.goodsType = type;
},
//卡项相关
goodsSelect(data) {
if (data.stock <= 0) return;
let _buyCardGoodsData = this.$util.deepClone(this.buyCardGoodsData);
if (_buyCardGoodsData['sku_' + data.sku_id]) {
_buyCardGoodsData['sku_' + data.sku_id].num += 1;
} else {
_buyCardGoodsData['sku_' + data.sku_id] = data;
_buyCardGoodsData['sku_' + data.sku_id].num = 1;
}
this.$store.commit('buycard/setGoodsData', _buyCardGoodsData);
this.$store.commit('buycard/setActive', 'SelectGoodsAfter');
},
getOnceCard() {
this.isLoad = false;
if (this.onceCardData.page + 1 > this.onceCardData.total) return;
this.onceCardData.page += 1;
getCardList({
page: this.onceCardData.page,
page_size: this.pageSize,
card_type: 'oncecard',
goods_state: 1,
status: 1,
}).then(res => {
if (res.code == 0) {
this.isLoad = true;
this.onceCardData.total = res.data.page_count || 1;
if (this.onceCardData.page == 1) this.onceCardData.list = [];
if (res.data.list.length) this.onceCardData.list = this.onceCardData.list.concat(res.data.list);
}
});
},
getTimeCard() {
if (this.timeCardData.page + 1 > this.timeCardData.total) return;
this.timeCardData.page += 1;
getCardList({
page: this.timeCardData.page,
card_type: 'timecard',
goods_state: 1,
page_size: this.pageSize,
status: 1,
}).then(res => {
if (res.code == 0) {
this.timeCardData.total = res.data.page_count || 1;
if (this.timeCardData.page == 1) this.timeCardData.list = [];
if (res.data.list.length) this.timeCardData.list = this.timeCardData.list.concat(
res.data.list);
}
});
},
getCommonCard() {
if (this.commonCardData.page + 1 > this.commonCardData.total) return;
this.commonCardData.page += 1;
getCardList({
page: this.commonCardData.page,
card_type: 'commoncard',
goods_state: 1,
page_size: this.pageSize,
status: 1,
}).then(res => {
if (res.code == 0) {
this.commonCardData.total = res.data.page_count || 1;
if (this.commonCardData.page == 1) this.commonCardData.list = [];
if (res.data.list.length) this.commonCardData.list = this.commonCardData.list.concat(res.data.list);
}
});
},
}
};

View File

@@ -0,0 +1,228 @@
.container {
height: 100%;
}
.header-action {
padding: 0.22rem 0.24rem;
display: flex;
align-items: center;
justify-content: space-between;
background-color: #fff;
border-radius: 0.04rem;
.header-action-left {
display: flex;
align-items: center;
height: 0.44rem;
background-color: var(--primary-color-light-9);
border-radius: 0.22rem;
view {
min-width: 1.02rem;
height: 0.44rem;
line-height: 0.44rem;
text-align: center;
font-size: 0.14rem;
border-left-width: 0;
transition: all 0.3s;
cursor: pointer;
border-radius: 0.22rem;
color: $primary-color;
&.active {
color: #fff;
background-color: $primary-color;
}
&:first-child {
border-left-width: 0.01rem;
box-shadow: none;
}
}
}
}
.content {
margin-top: .2rem;
box-sizing: border-box;
height: calc(100% - 1.08rem);
transform: rotate(0);
display: flex;
flex-direction: column;
}
.list-wrap {
flex: 1;
height: 0;
}
.table-list {
display: flex;
align-items: center;
flex-wrap: wrap;
.table-item {
border: 0.01rem solid #fff;
box-sizing: border-box;
padding: 0.1rem 0.18rem 0.1rem 0.1rem;
background-color: #fff;
margin-bottom: 0.12rem;
margin-right: 0.12rem;
cursor: pointer;
transition: border-color, background-color 0.3s;
position: relative;
border-radius: 0.04rem;
&.item-mum-2{
width: calc((100% - 0.25rem) / 2);
}
&.item-mum-3{
width: calc((100% - 0.37rem) / 3);
}
&.item-mum-4{
width: calc((100% - 0.49rem) / 4);
}
.item-other {
display: flex;
flex-wrap: wrap;
margin-left: 0.1rem;
}
.item-img {
width: 0.9rem;
height: 0.9rem;
display: flex;
align-items: center;
overflow: hidden;
-ms-flex-negative: 0;
-webkit-flex-shrink: 0;
flex-shrink: 0;
image {
width: 100%;
border-radius: 0.03rem;
}
}
.item-name {
height: 0.4rem;
line-height: 0.2rem;
max-width: 1.58rem;
margin-bottom: 0.05rem;
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.no-stock {
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.5);
cursor: not-allowed;
image{
height: 60%;
}
}
.item-info {
cursor: pointer;
flex: 1;
display: flex;
.item-time {
font-size: 0.12rem;
color: #909399;
}
.item-money {
font-size: 0.14rem;
color: $primary-color;
height: 0.19rem;
line-height: 0.19rem;
}
.item-stock {
height: 0.17rem;
font-size: 0.12rem;
color: #808695;
line-height: 0.17rem;
}
}
}
.table-item.yes-stock {
&:hover {
background-color: var(--primary-color-light-9);
border-color: $primary-color;
}
&.focus,
&:focus {
background-color: var(--primary-color-light-9);
border-color: $primary-color;
outline: none;
}
&.active {
border-color: $primary-color;
background-color: $primary-color;
color: #fff;
.item-time,
.item-money,
.item-stock {
color: #fff;
}
}
}
}
.empty {
text-align: center;
padding-top: 1.2rem;
image {
width: 2rem;
}
.tips {
color: #999;
margin-top: 0.15rem;
}
}
/deep/ .uni-scroll-view {
&::-webkit-scrollbar {
width: 0.06rem;
height: 0.06rem;
background-color: rgba($color: #000000, $alpha: 0);
}
&::-webkit-scrollbar-button {
display: none;
}
&::-webkit-scrollbar-thumb {
border-radius: 0.06rem;
box-shadow: inset 0 0 0.06rem rgba(45, 43, 43, 0.45);
background-color: #ddd;
display: none;
}
&:hover::-webkit-scrollbar-thumb {
display: block;
}
&::-webkit-scrollbar-track {
background-color: transparent;
}
}

View File

@@ -0,0 +1,111 @@
<template>
<view class="container">
<view class="header-action common-wrap">
<view class="header-action-left">
<view :class="{ active: goodsType == 'oncecard' }" @click="switchGoodsType('oncecard')">限次卡</view>
<view :class="{ active: goodsType == 'timecard' }" @click="switchGoodsType('timecard')">限时卡</view>
<view :class="{ active: goodsType == 'commoncard' }" @click="switchGoodsType('commoncard')">通用卡</view>
</view>
</view>
<view class="content">
<scroll-view scroll-y="true" class="list-wrap" @scrolltolower="getOncecard()" v-show="goodsType == 'oncecard'">
<view class="table-list" v-show="onceCardData.list.length > 0">
<view class="table-item" :class="{'yes-stock': item.stock>0, 'item-mum-2': itemNum == 2, 'item-mum-3': itemNum == 3, 'item-mum-4': itemNum == 4, 'active': selectCardSkuId.indexOf(item.sku_id) > -1 }" v-for="(item, index) in onceCardData.list" :key="index" @click="goodsSelect(item)">
<view class="item-info">
<view class="item-img">
<image v-if="item.goods_image == '@/static/goods/goods.png'" src="@/static/goods/goods.png" mode="widthFix"/>
<image v-else :src="$util.img(item.goods_image.split(',')[0], { size: 'small' })" @error="item.goods_image = '@/static/goods/goods.png'" mode="widthFix"/>
</view>
<view class="item-other flex-1">
<view class="item-name">{{ item.goods_name }}</view>
<view class="w-full self-end">
<view class="item-money">
<text class="util"></text>
{{ item.discount_price | moneyFormat }}
</view>
</view>
</view>
</view>
<view class="no-stock" v-if="item.stock <= 0">
<image src="@/static/stock/stock_empty.png" mode="heightFix"/>
</view>
</view>
</view>
<view class="empty" v-if="isLoad && !onceCardData.list.length">
<image src="@/static/goods/goods_empty.png" mode="widthFix"/>
<view class="tips">暂无卡项</view>
</view>
</scroll-view>
<scroll-view scroll-y="true" class="list-wrap" @scrolltolower="getTimecard()" v-show="goodsType == 'timecard'">
<view class="table-list" v-show="timeCardData.list.length > 0">
<view class="table-item" :class="{'yes-stock': item.stock>0, 'item-mum-2': itemNum == 2, 'item-mum-3': itemNum == 3, 'item-mum-4': itemNum == 4,}" v-for="(item, index) in timeCardData.list" :key="index" @click="goodsSelect(item)">
<view class="item-info">
<view class="item-img">
<image v-if="item.goods_image == '@/static/goods/goods.png'" src="@/static/goods/goods.png" mode="widthFix"/>
<image v-else :src="$util.img(item.goods_image.split(',')[0], { size: 'small' })" @error="item.goods_image = '@/static/goods/goods.png'" mode="widthFix"/>
</view>
<view class="item-other flex-1">
<view class="item-name">{{ item.goods_name }}</view>
<view class="w-full self-end">
<view class="item-money">
<text class="util"></text>
{{ item.discount_price | moneyFormat }}
</view>
</view>
</view>
</view>
<view class="no-stock" v-if="item.stock <= 0">
<image src="@/static/stock/stock_empty.png" mode="heightFix"/>
</view>
</view>
</view>
<view class="empty" v-if="!timeCardData.list.length">
<image src="@/static/goods/goods_empty.png" mode="widthFix"/>
<view class="tips">暂无卡项</view>
</view>
</scroll-view>
<scroll-view scroll-y="true" class="list-wrap" @scrolltolower="getCommoncard()" v-show="goodsType == 'commoncard'">
<view class="table-list" v-show="commonCardData.list.length > 0">
<view class="table-item" :class="{'yes-stock': item.stock>0, 'item-mum-2': itemNum == 2, 'item-mum-3': itemNum == 3, 'item-mum-4': itemNum == 4,}" v-for="(item, index) in commonCardData.list" :key="index" @click="goodsSelect(item)">
<view class="item-info">
<view class="item-img">
<image v-if="item.goods_image == '@/static/goods/goods.png'" src="@/static/goods/goods.png" mode="widthFix"/>
<image v-else :src="$util.img(item.goods_image.split(',')[0], { size: 'small' })" @error="item.goods_image = '@/static/goods/goods.png'" mode="widthFix"/>
</view>
<view class="item-other flex-1">
<view class="item-name">{{ item.goods_name }}</view>
<view class="w-full self-end">
<view class="item-money">
<text class="util"></text>
{{ item.discount_price | moneyFormat }}
</view>
</view>
</view>
</view>
<view class="no-stock" v-if="item.stock <= 0">
<image src="@/static/stock/stock_empty.png" mode="heightFix"/>
</view>
</view>
</view>
<view class="empty" v-if="!commonCardData.list.length">
<image src="@/static/goods/goods_empty.png" mode="widthFix"/>
<view class="tips">暂无卡项</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import index from './index.js';
export default {
mixins: [index]
};
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

View File

@@ -0,0 +1,520 @@
<template>
<unipopup ref="dialogRef" type="center" :maskClick="false">
<view class="stock-dialog-wrap">
<view class="stock-dialog-head">
<text>商品选择</text>
<text class="iconfont iconguanbi1" @click="$emit('change', false)"></text>
</view>
<view class="stock-dialog-body">
<view class="tree">
<scroll-view scroll-y="true" class="list-wrap">
<view class="item" :class="{ 'active': option.category_id === '' }" @click="itemClick({ category_id: '', child_num: 0 })">
<view class="icon"></view>
<view>全部分类</view>
</view>
<view v-for="(item, key) in goodsCategoryList" :key="key">
<view class="item" :class="{ 'active': option.category_id === item.category_id }" @click="itemClick(item)">
<view class="icon" :class="{ 'active': activeList.indexOf(item.category_id) != -1 }">
<text v-if="item.child_num" class="iconfont iconsanjiao_xia"></text>
</view>
<view>{{ item.title }}</view>
</view>
<template v-if="item.child_num">
<view v-show="activeList.indexOf(item.category_id) != -1" v-for="(item2, key2) in item.children" :key="key2" class="level">
<view class="item" :class="{ 'active': option.category_id === item2.category_id }" @click="itemClick(item2)">
<view class="icon" :class="{ 'active': activeList.indexOf(item2.category_id) != -1 }">
<text v-if="item2.child_num" class="iconfont iconsanjiao_xia"></text>
</view>
<view>{{ item2.title }}</view>
</view>
<template>
<view v-show="activeList.indexOf(item2.category_id) != -1" v-for="(item3, key3) in item2.children" :key="key3" class="level">
<view class="item item2" @click="itemClick(item3)">
<view class="icon"></view>
<view>{{ item3.title }}</view>
</view>
</view>
</template>
</view>
</template>
</view>
</scroll-view>
</view>
<view class="stock-dialog-table">
<view class="search common-form">
<view class="common-form-item">
<view class="form-input-inline" v-if="isInstallSupply == 1">
<select-lay :zindex="10" :value="option.supplier_id" name="supplier_id" placeholder="请选择供应商" :options="supplierList" @selectitem="selectSupplier"/>
</view>
<view class="form-input-inline">
<select-lay :zindex="10" :value="option.brand_id" name="brand_id" placeholder="请选择品牌" :options="brandList" @selectitem="selectBrand"/>
</view>
<view class="form-input-inline">
<select-lay :zindex="10" :value="option.goods_class" name="goods_class" placeholder="请选择类型" :options="goodsClassList" @selectitem="selectGoodsClass"/>
</view>
<view class="form-inline">
<view class="form-input-inline">
<input type="text" v-model="option.search_text" @confirm="getStoreGoods" placeholder="请输入名称/编码" class="form-input" />
</view>
</view>
<view class="form-inline common-btn-wrap">
<button type="default" class="screen-btn" @click="getStoreGoods">筛选</button>
</view>
</view>
</view>
<uniDataTable class="goods-table" pk="sku_id" :url="url" :option="option" :cols="cols" :pagesize="8" ref="goodsListTable"></uniDataTable>
</view>
</view>
<view class="btn">
<button type="primary" class="primary-btn submit" @click="submit('close')">选中</button>
<button type="primary" class="default-btn" @click="$emit('change', false)">取消</button>
</view>
</view>
</unipopup>
</template>
<script>
import unipopup from '@/components/uni-popup/uni-popup.vue';
import uniDataTable from '@/components/uni-data-table/uni-data-table-new.vue';
import {getManageGoodsCategory,getGoodsSceen,getSkuListBySelect} from '@/api/goods.js';
export default {
name: 'stockDialog',
components: {
unipopup,
uniDataTable
},
model: {
prop: 'value',
event: 'change'
},
props: {
value: {
type: Boolean,
default: false
},
params: {
type: Object,
default: ()=>{
return {}
}
},
goodsClass:{
type: Array,
default: ()=>{
return [1,4,5,6];
},
}
},
data() {
return {
goodsCategoryList: {},
activeList: [],//下拉激活
option: {
category_id: '',
search_text: '',
is_weigh: 0,
page_size: 8,
goods_class_all:'',
goods_class:'',
supplier_id:'',
brand_id:'',
},
checkList: {},
cols: [],
url: '',
goodsClassList: [],
isInstallSupply:0,
supplierList:[],
brandList:[],
}
},
watch: {
value: {
handler: function (val) {
if (val) {
this.$nextTick(() => {
this.option = Object.assign(this.option, this.params);
if (this.params.temp_store_id && this.params.temp_store_id == '') {
delete this.option.temp_store_id
}
this.$refs.dialogRef.open()
})
} else {
this.$nextTick(() => {
this.option = Object(this.option, {
category_id: '',
search_text: '',
is_weigh: 0,
page: 1,
page_size: 8,
});
this.checkList = {};
this.$refs.dialogRef.close()
})
}
},
immediate: true
},
},
mounted() {
this.skuConfig();
this.getGoodsCategory();
this.getScreen();
this.getGoodsClassList();
},
methods: {
skuConfig(){
this.cols = [{
width: 20,
align: 'center',
checkbox: true,
}, {
field: 'account_data',
width: 50,
title: '商品信息',
align: 'left',
templet: data => {
let img = this.$util.img(data.sku_image);
let html = `
<view class="goods-content">
<image class="goods-img" src="${img}" mode="aspectFit"/>
<text class="goods-name multi-hidden" title="${data.sku_name}">${data.sku_name}</text>
</view>
`;
return html;
}
}, {
field: 'stock',
width: 22,
title: '库存',
align: 'center',
templet: data => {
return (data.stock || 0);
}
}, {
width: 22,
title: '单位',
templet: data => {
return (data.unit || '件');
}
}];
this.url = '/cashier/storeapi/goods/getSkuListBySelect';
},
selectGoodsClass(index) {
this.option.goods_class = index == -1 ? '' : this.goodsClassList[index].value.toString();
this.getStoreGoods();
},
selectBrand(index){
this.option.brand_id = index == -1 ? '' : this.brandList[index].value.toString();
this.getStoreGoods();
},
selectSupplier(index){
this.option.supplier_id = index == -1 ? '' : this.supplierList[index].value.toString();
this.getStoreGoods();
},
getGoodsCategory() {
getManageGoodsCategory().then(res=>{
uni.hideLoading();
if (res.data && Object.keys(res.data)) {
this.goodsCategoryList = res.data
} else {
this.$util.showToast({
title: res.message
});
}
})
},
getScreen(){
getGoodsSceen().then(res=>{
this.isInstallSupply = res.data.is_install_supply;
(res.data.supplier_list || []).forEach((item) => {
this.supplierList.push({
value : item.supplier_id,
label : item.title,
})
})
res.data.brand_list.forEach((item) => {
this.brandList.push({
value : item.brand_id,
label : item.brand_name,
})
})
})
},
getGoodsClassList(){
let goodsClassList = [
{
value: this.$util.goodsClassDict.real,
label: '实物商品'
}, {
value: this.$util.goodsClassDict.service,
label: '服务项目'
}, {
value: this.$util.goodsClassDict.card,
label: '卡项套餐'
}, {
value: this.$util.goodsClassDict.weigh,
label: '称重商品'
}
];
let goods_class_all = [];
goodsClassList.forEach((item)=>{
if(this.goodsClass.indexOf(item.value) > -1){
this.goodsClassList.push(item);
goods_class_all.push(item.value);
}
})
this.option.goods_class_all = goods_class_all.toString();
},
itemClick(item) {//tree点击
this.option.category_id = item.category_id;
var index = this.activeList.indexOf(item.category_id);
if (item.child_num && index === -1) {
this.activeList.push(item.category_id);
} else if (item.child_num && index != -1) {
this.activeList.splice(index, 1);
}
this.$forceUpdate();
this.getStoreGoods();
},
getStoreGoods() {//表格查询
this.$refs.goodsListTable.load({
page: 1
});
},
submit(action) {
let res = this.$refs.goodsListTable.getSelectData();
if(res.selectedNum == 0){
this.$util.showToast({
title: '请选择商品'
});
return false
}
if(res.allSelected){
let option = this.$util.deepClone(this.option);
option.unselected_sku_ids = Object.keys(res.unselectedData).toString();
option.page_size = 0;
uni.showLoading({
title: '数据获取中'
});
getSkuListBySelect(option).then(res=>{
uni.hideLoading();
this.$emit('selectGoods', res.data.list);
this.$refs.goodsListTable.clearCheck();
if(action == 'close'){
this.$emit('change', false);
}
})
}else{
this.$emit('selectGoods', Object.values(res.selectedData));
this.$refs.goodsListTable.clearCheck();
if(action == 'close'){
this.$emit('change', false);
}
}
}
}
}
</script>
<style lang="scss" scoped>
.stock-dialog-wrap {
background-color: #fff;
border-radius: 0.05rem;
width: 100%;
height: 75vh;
.stock-dialog-head {
padding: 0 0.15rem;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 0.15rem;
height: 0.45rem;
border-bottom: 0.01rem solid #e8eaec;
.iconguanbi1 {
font-size: $uni-font-size-lg;
}
}
.stock-dialog-body {
width: 100%;
// height: 7.3rem;
height: calc(100% - 0.45rem - 0.58rem);
padding: 0.1rem 0.2rem 0 0.2rem;
box-sizing: border-box;
display: flex;
.tree {
width: 1.8rem;
// height: 7.1rem;
height: 100%;
overflow-y: auto;
border-right: 0.01rem solid #e8eaec;
flex-shrink: 0;
flex-basis: auto;
flex-grow: 0;
box-sizing: border-box;
.list-wrap {
width: 100%;
height: 100%;
>view {
box-sizing: border-box;
width: 100%;
}
view.item {
display: flex;
align-items: center;
width: 100%;
box-sizing: border-box;
line-height: 0.3rem;
min-height: 0.3rem;
font-weight: 500;
&.active {
.icon,
view {
color: $primary-color !important;
}
background-color: #f7f7f7;
}
&:hover {
background-color: #f7f7f7;
}
.icon {
width: 0.2rem;
height: 0.3rem;
display: flex;
align-items: center;
justify-content: center;
transform: rotate(-90deg);
transition: all ease 0.5s;
&.active {
transform: rotate(-45deg);
}
}
}
.level {
width: 100%;
box-sizing: border-box;
.item {
padding-left: 0.2rem;
}
.item2 {
padding-left: 0.4rem;
}
}
}
}
.stock-dialog-table {
width: 6.6rem;
margin-left: 0.2rem;
display: flex;
flex-direction: column;
height: 100%;
.search {
display: flex;
justify-content: flex-end;
}
.goods-table /deep/{
flex: 1;
height: 0;
.tbody{
height: calc( 100% - 0.5rem - 0.5rem );
overflow-y: auto;
&::-webkit-scrollbar{
width: 0.06rem;
height: 0.06rem;
background-color: rgba(0, 0, 0, 0);
}
&::-webkit-scrollbar-button{
display: none;
}
&::-webkit-scrollbar-thumb{
border-radius: 0.06rem;
box-shadow: inset 0 0 0.06rem rgba(45, 43, 43, 0.45);
background-color: #ddd;
}
&::-webkit-scrollbar-track{
background-color: transparent;
}
}
}
}
}
.btn {
display: flex;
justify-content: flex-end;
border-top: 0.01rem solid #e8eaec;
padding: 0.1rem 0.2rem 0.1rem 0.2rem;
box-sizing: border-box;
height: 0.58rem;
.default-btn,
.primary-btn {
margin: 0;
}
.default-btn {
border: 0.01rem solid #e8eaec !important;
}
.submit{
margin-right: 0.15rem;
}
.default-btn::after {
display: none;
}
}
.common-form{
.common-btn-wrap {
margin-left: 0;
.screen-btn {
margin-right: 0;
padding-left:14px;
padding-right:14px;
}
}
.common-form-item {
margin-bottom: 0.1rem;
.form-input-inline {
width: 1.3rem;
}
}
}
/deep/ .goods-content {
display: flex;
.goods-img {
margin-right: 0.1rem;
width: 0.5rem;
height: 0.5rem;
flex-shrink: 0;
flex-basis: auto;
flex-grow: 0;
}
}
}
</style>

View File

@@ -0,0 +1,168 @@
<template>
<view class="ns-record">
<view class="title">
库存记录
<text class="iconfont iconguanbi1" @click="close"></text>
</view>
<view class="table">
<view class="table-th">
<view class="table-td" style="width: 15%;">规格名称</view>
<view class="table-td" style="width: 12%;">业务类型</view>
<view class="table-td" style="width: 14%;">原始数量</view>
<view class="table-td" style="width: 14%;">变动数量</view>
<view class="table-td" style="width: 14%;">剩余数量</view>
<view class="table-td" style="width: 14%;">入库单价</view>
<view class="table-td" style="width: 17%;">创建时间</view>
</view>
<scroll-view scroll-y="true" class="table-tb">
<view class="table-tr" v-for="(item, index) in list" :key="index">
<view class="table-td" style="width: 15%;">{{ item.spec_name ? item.spec_name : item.goods_name }}</view>
<view class="table-td" style="width: 12%;">{{ item.name }}</view>
<view class="table-td" style="width: 14%;">{{ item.before_store_stock }}</view>
<view class="table-td" style="width: 14%;">{{ item.goods_num }}</view>
<view class="table-td" style="width: 14%;">{{ item.after_store_stock }}</view>
<view class="table-td" style="width: 14%;">{{ item.goods_price }}</view>
<view class="table-td" style="width: 17%;">{{ $util.timeFormat(item.create_time) }}</view>
</view>
</scroll-view>
</view>
<!-- 分页 -->
<view class="pagination">
<uni-pagination @change="changePage" :pageSize="page_size" show-icon="true" :total="total" :current="page"/>
</view>
</view>
</template>
<script>
import {getStockGoodsRecords} from '@/api/stock.js'
export default {
data() {
return {
page: 1,
page_size: 8,
list: [],
total: 0
};
},
props: {
goodsId: {
type: Number,
default: () => {
return 0;
}
}
},
mounted() {
this.getData();
},
methods: {
// 分页发生变化
changePage(e) {
this.page = e.current;
this.getData();
},
// 获取数据
getData() {
getStockGoodsRecords({
page: this.page,
page_size: this.page_size,
goods_id: this.goodsId,
}).then(res => {
if (res.code >= 0) {
this.total = res.data.count;
this.list = res.data.list.map((item, index) => {
let unit = '';
if (item.type == 'input') {
unit = '+';
} else {
unit = '-';
}
item.goods_num = unit + item.goods_num;
return item;
});
}
})
},
// 弹窗关闭
close() {
this.$emit('close');
}
}
};
</script>
<style lang="scss" scoped>
.ns-record {
width: 100%;
height: 100%;
background: #ffffff;
border-radius: 0.04rem;
min-height: 2rem;
padding-bottom: 0.4rem;
.title {
width: 100%;
height: 0.5rem;
border-bottom: 0.01rem solid #e6e6e6;
font-size: 0.16rem;
line-height: 0.5rem;
text-align: center;
position: relative;
font-weight: bold;
.iconguanbi1 {
font-size: 0.2rem;
position: absolute;
top: 50%;
right: 0.15rem;
transform: translateY(-50%);
font-weight: 500;
}
}
.table {
width: 100%;
height: 4rem;
padding: 0 0.15rem;
box-sizing: border-box;
.table-th {
width: 100%;
height: 0.5rem;
display: flex;
align-items: center;
justify-content: space-between;
background: #f7f8fa;
padding: 0 0.15rem;
box-sizing: border-box;
}
.table-tb {
width: 100%;
height: calc(100% - 0.5rem);
.table-tr {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 0.5rem;
padding: 0 0.15rem;
box-sizing: border-box;
border-bottom: 0.01rem solid #e6e6e6;
}
}
}
}
/deep/ .uni-date-single {
height: 0.3rem;
}
.table-td {
height: 100%;
line-height: 0.5rem;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size: 0.14rem;
}
.pagination {
width: 100%;
margin-top: 0.2rem;
}
</style>

View File

@@ -0,0 +1,268 @@
<template>
<view class="ns-record">
<view class="title">
价格库存
<text class="iconfont iconguanbi1" @click="close"></text>
</view>
<view class="table">
<view class="table-th">
<view class="table-td" style="min-width: 35%;max-width: 35%;">规格名称</view>
<view class="table-td">统一售价</view>
<view class="table-td" v-if="isUnifyPrice == 0">独立售价</view>
<view class="table-td">库存</view>
<view class="table-td" v-if="disabled">限制起送</view>
</view>
<scroll-view scroll-y="true" class="table-tb">
<view class="table-tr" v-for="(item, index) in skuList" :key="index">
<view class="table-td" style="min-width: 35%;max-width: 35%;">
{{ item.spec_name ? item.spec_name : item.goods_name }}</view>
<view class="table-td">{{ item.discount_price }}</view>
<view class="table-td input-wrap" v-if="isUnifyPrice == 0">
<input class="input" v-model="item.store_price" type="digit" />
<view class="unit"></view>
</view>
<view class="table-td input-wrap" v-if="!disabled">
<block v-if="globalStoreInfo.stock_type == 'store'">
<input class="input" v-model="item.stock" type="digit" :disabled="disabled" />
<view class="unit">{{ item.unit ? item.unit : '件' }}</view>
</block>
<view v-else>{{ item.stock }}</view>
</view>
<view class="table-td input-wrap" v-else>
<view>{{ item.stock }}</view>
</view>
<view class="table-td input-wrap" v-if="disabled">
<switch @change="changeswitch($event,index)" color="#FA6400" style="transform:scale(0.7)"
:checked="item.is_delivery_restrictions == 1" />
</view>
</view>
</scroll-view>
</view>
<view class="pop-bottom"><button class="primary-btn" @click="save()">确定</button></view>
</view>
</template>
<script>
import {
editGoods,
setGoodsLocalRestrictions
} from '@/api/goods.js';
export default {
data() {
return {
goodsSkuList: [],
flag: true
};
},
props: {
skuList: {
type: Array,
default: () => {
return [];
}
},
isUnifyPrice: {
type: Number,
default: () => {
return 0;
}
},
disabled: {
type: Boolean,
default: () => {
return false;
}
},
},
created() {},
methods: {
changeswitch(e, index) {
this.skuList[index].is_delivery_restrictions = e.detail.value ? 1 : 0
},
getGoodsSku() {
this.goodsSkuList = [];
Object.keys(this.skuList).forEach(key => {
let data = this.skuList[key];
let obj = {}
if (this.disabled) {
obj.sku_id = data.sku_id
obj.is_delivery_restrictions = data.is_delivery_restrictions
} else {
obj.sku_id = data.sku_id
obj.price = data.store_price ? data.store_price : data.discount_price
}
if (this.globalStoreInfo.stock_type == 'store') {
obj.stock = data.stock;
}
this.goodsSkuList.push(obj);
});
},
save() {
this.getGoodsSku();
if (!this.flag) return false;
this.flag = false;
uni.showLoading({
title: '请求处理中'
});
if (this.disabled) {
setGoodsLocalRestrictions({
goods_sku_list: JSON.stringify(this.goodsSkuList)
}).then(res => {
uni.hideLoading();
this.$util.showToast({
title: res.message
});
if (res.code >= 0) {
this.$root.$refs.goodsListTable.load();
this.close();
} else {
this.flag = true;
}
})
} else {
editGoods({
goods_sku_list: JSON.stringify(this.goodsSkuList)
}).then(res => {
uni.hideLoading();
this.$util.showToast({
title: res.message
});
if (res.code >= 0) {
this.$root.$refs.goodsListTable.load();
this.close();
} else {
this.flag = true;
}
})
}
},
// 弹窗关闭
close() {
this.$emit('close');
}
}
};
</script>
<style lang="scss" scoped>
.ns-record {
width: 100%;
height: 100%;
background: #ffffff;
border-radius: 0.04rem;
min-height: 2rem;
.title {
width: 100%;
height: 0.5rem;
border-bottom: 0.01rem solid #e6e6e6;
font-size: 0.16rem;
line-height: 0.5rem;
text-align: center;
position: relative;
font-weight: bold;
.iconguanbi1 {
font-size: 0.2rem;
position: absolute;
top: 50%;
right: 0.15rem;
transform: translateY(-50%);
font-weight: 500;
}
}
.table {
width: 100%;
height: 4rem;
padding: 0.15rem;
box-sizing: border-box;
.table-th {
width: 100%;
height: 0.5rem;
display: flex;
align-items: center;
justify-content: space-between;
background: #f7f8fa;
box-sizing: border-box;
}
.table-td {
display: flex;
flex: 1;
padding: 0 0.15rem;
}
.table-tb {
width: 100%;
height: calc(100% - 0.5rem);
.table-tr {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 0.5rem;
box-sizing: border-box;
border-bottom: 0.01rem solid #e6e6e6;
}
}
}
}
/deep/ .uni-date-single {
height: 0.3rem;
}
.input-wrap {
height: 100%;
line-height: 0.5rem;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size: 0.14rem;
display: flex;
align-items: center;
.input {
width: 0.7rem;
height: 0.35rem;
border: 0.01rem solid #e6e6e6;
padding: 0 0.1rem;
border-top-left-radius: 0.03rem;
border-bottom-left-radius: 0.03rem;
}
.unit {
background-color: #eee;
height: 0.35rem;
width: 0.35rem;
text-align: center;
line-height: 0.35rem;
border: 0.01rem solid #e6e6e6;
border-left: 0;
border-top-right-radius: 0.03rem;
border-bottom-right-radius: 0.03rem;
}
}
.save {
height: 0.3rem;
line-height: 0.3rem;
}
.pop-bottom {
padding: 0.1rem 0.2rem;
border-top: 0.01rem solid #eee;
button {
width: 100%;
line-height: 0.35rem;
height: 0.35rem;
}
}
</style>

View File

@@ -0,0 +1,828 @@
var self;
import {
getGoodsList,
getGoodsCategory,
getServiceCategory,
getServiceList,
getGoodsSkuList,
getElectronicScaleInformation,
getGoodsInfoByCode
} from '@/api/goods'
import {mapGetters} from 'vuex';
export default {
name: 'nsGoods',
props: {
indexFocus: {
type: [String, Number],
default: ''
}
},
data() {
return {
type: 'goods',
serviceCategory: [],
serviceCategoryId: 'all',
serviceData: {
size: 12,
index: 1,
total: 0,
list: []
},
goodsCategoryId: 'all',
goodsCategory: [],
goodsData: {
size: 30,
index: 1,
total: 0,
list: []
},
skuInfo: null,
allSku: null,
searchText: '',
itemNum: 3,
mediaQueryOb: null,
paymentMoney: '',
cashierScale: null,
actionIndex: 0,
inputFocus: false,
goodsCategoryShow: false,
goodsCategoryIndex: 0,
serviceCategoryShow: false,
serviceCategoryIndex: 0,
scanCode: {
code: '',
lastTime: 0
},
isGoodsLoad: false,
goodsItems:{},//点击商品后如果是可转换库存商品存储商品数据
scanCodeSearch:false,
goodsNextPage: true,
};
},
watch: {
serviceCategoryId: function (nval) {
this.serviceData.page = 0;
this.serviceData.index = 1;
this.$refs.loading.show();
this.getService();
},
goodsCategoryId: function (nval) {
this.goodsData.page = 0;
this.goodsData.index = 1;
this.$refs.goodsLoading.show();
this.getGoods();
},
type: function () {
this.searchText = '';
}
},
computed: {
goodsSpec() {
if (this.allSku && this.skuInfo) {
let data = [];
if (this.skuInfo.goods_class != 6 || (this.skuInfo.goods_class == 6 && this.skuInfo.goods_spec_format)) {
data = this.allSku['sku_id_' + this.skuInfo.sku_id].goods_spec_format;
}
return data;
}
return [];
},
...mapGetters(['billingGoodsIds', 'billingGoodsData', 'billingActive', 'billingIsScanTrigger','billingIsShowCashBox'])
},
created() {
this.init();
this.addScanCodeEvent();
this.addKeyDownEvent();
},
mounted() {
this.mediaQueryOb = uni.createMediaQueryObserver(this);
this.mediaQueryOb.observe({maxWidth: 1500}, matches => {
if (matches) this.itemNum = 2;
});
this.mediaQueryOb.observe({minWidth: 1501, maxWidth: 1700}, matches => {
if (matches) this.itemNum = 3;
});
this.mediaQueryOb.observe({minWidth: 1701}, matches => {
if (matches) this.itemNum = 4;
});
document.addEventListener('click', this.isListShow)
},
beforeDestroy() {
document.removeEventListener("click", this.isListShow);
},
methods: {
nextGoodsLiist(){
if(!this.goodsNextPage) return;
this.goodsData.index ++;
this.getGoods();
},
init() {
self = this;
this.getServiceCategoryFn();
this.getGoodsCategoryFn();
this.getService();
this.getGoods();
},
getServiceCategoryFn() {
getServiceCategory().then(res => {
if (res.code == 0 && res.data) {
this.serviceCategory = res.data;
}
});
},
getGoodsCategoryFn() {
getGoodsCategory().then(res => {
if (res.code == 0 && res.data) {
this.goodsCategory = res.data;
}
});
},
getService() {
getServiceList({
page: this.serviceData.index,
page_size: this.serviceData.size,
category: this.serviceCategoryId,
search_text: this.searchText,
status: 1
}).then(res => {
if (this.$refs.loading) this.$refs.loading.hide();
if (res.code == 0) {
this.serviceData.total = res.data.count;
this.serviceData.list = res.data.list || [];
this.searchText = '';
}
});
},
getGoods() {
this.isGoodsLoad = false;
getGoodsList({
page: this.goodsData.index,
page_size: this.goodsData.size,
category: this.goodsCategoryId,
search_text: this.searchText,
goods_class: '1,6',
status: 1,
scene:'billing',
}).then((res) => {
if (this.$refs.goodsLoading) this.$refs.goodsLoading.hide();
if (res.code == 0) {
this.goodsData.total = res.data.count;
// this.goodsData.list = res.data.list || [];
res.data.list.forEach(item => {
item.adjust = {}; // 存储规格调价
});
if (this.goodsData.index == 1) this.goodsData.list = []; //如果是第一页需手动制空列表
this.goodsData.list.push(...res.data.list);
if(res.data.list.length) this.goodsNextPage = true;
else this.goodsNextPage = false;
if(this.scanCodeSearch) this.searchText = '';
this.scanCodeSearch = false;
}
this.isGoodsLoad = true;
})
},
goodsSelect(data, index) {
if (this.type == 'goods' && !data.stock) return;
if (index != undefined) this.actionIndex = index;
if (data.goods_class != 6 && data.adjust && data.adjust['sku_id_' + data.sku_id]) {
data.adjust_price = data.adjust['sku_id_' + data.sku_id].adjust_price;
data.is_adjust = data.adjust['sku_id_' + data.sku_id].is_adjust;
}
// 满足条件:多规格,称重商品计重模式,编辑数量
if (data.goods_spec_format || (data.goods_class == 6 && data.pricing_type == 'weight') || (data.status && data.status == 'edit')) {
this.setActive('SelectGoodsSku');
getGoodsSkuList(data.goods_id).then(res => {
if (res.code == 0) {
let obj = {};
res.data.forEach(item => {
try{
item.goods_spec_format = JSON.parse(item.goods_spec_format);
}catch(e){
item.goods_spec_format = [];
}
obj['sku_id_' + item.sku_id] = item;
});
this.allSku = obj;
this.skuInfo = obj['sku_id_' + data.sku_id];
// 调整价格,称重商品不参与永久调价,每次都是初始原价
if (data.goods_class != 6 && data.adjust_price) {
this.skuInfo.adjust_price = data.adjust_price; // 使用调价
} else {
this.skuInfo.adjust_price = data.price; // 未调价,使用原价
}
this.skuInfo.adjust_price = parseFloat(this.skuInfo.adjust_price).toFixed(2);
this.$set(this.skuInfo, 'status', data.status || '');
this.$set(this.skuInfo, 'editKey', data.editKey || '');
if (data.goods_class != 6) {
this.skuInfo.num = data.num || 1; // 默认购买数量为1
}
// 称重商品,计重模式
if (data.goods_class == 6 && data.pricing_type == 'weight') {
let num = data.num || '';
if (num) {
this.$set(this.skuInfo, 'weigh', num);
Object.values(this.allSku).forEach((item, index) => {
this.$set(item, 'weigh', num);
});
}
// 打开收银秤
this.openCashierScale()
}
this.$refs.skuPopup.open();
setTimeout(() => {
this.inputFocus = true;
}, 200);
}
});
} else {
this.handleSelectGoods(data);
this.scanCode = {
lastTime: 0,
code: ''
};
this.$store.commit('billing/setActive', 'SelectGoodsAfter'); // 记录页面当前活跃值:选择完商品
}
},
skuSelect(sku_id) {
if (!this.skuInfo.status) {
this.skuInfo = this.allSku['sku_id_' + sku_id];
this.goodsItems.sku_id = sku_id
let skuData = this.goodsData.list[this.actionIndex];
// 调整价格
if (skuData.adjust && skuData.adjust['sku_id_' + sku_id]) {
this.skuInfo.adjust_price = skuData.adjust['sku_id_' + sku_id].adjust_price;
this.skuInfo.is_adjust = skuData.adjust['sku_id_' + sku_id].is_adjust;
} else {
// 使用原价
this.skuInfo.adjust_price = this.skuInfo.price;
}
this.skuInfo.adjust_price = parseFloat(this.skuInfo.adjust_price).toFixed(2);
if (this.skuInfo.goods_class != 6) {
this.skuInfo.num = this.skuInfo.num || 1; // 默认购买数量为1
}
}
},
skuConfirm() {
if (!this.skuInfo) return;
if (this.skuInfo.stock <= 0) {
this.$util.showToast({
title: '商品库存不足'
});
return;
}
if (this.skuInfo.price.length == 0 || this.skuInfo.adjust_price.length == 0) {
this.$util.showToast({
title: '请输入单价'
});
return;
}
if (this.skuInfo.price < 0 || this.skuInfo.adjust_price < 0) {
this.$util.showToast({
title: '单价不能小于0'
});
return;
}
if (this.skuInfo.goods_class != 6) {
if (Number.parseInt(this.skuInfo.num) <= 0 || !/^\d{0,10}$/.test(Number.parseInt(this.skuInfo.num))) {
this.$util.showToast({
title: '请输入合法的数值,数值要大于零'
});
return;
}
if (this.skuInfo.stock < this.skuInfo.num) {
this.$util.showToast({
title: '商品库存不足'
});
return;
}
}
if (this.skuInfo.goods_class == 6 && this.skuInfo.pricing_type == 'weight') {
if (Number.parseFloat(this.skuInfo.weigh) <= 0 || !/^\d{0,10}(.?\d{0,3})$/.test(Number.parseFloat(this.skuInfo.weigh))) {
this.$util.showToast({
title: '请输入合法的数值,数值要大于零且小数位不能超过三位'
});
return;
}
if (this.skuInfo.stock < this.skuInfo.weigh) {
this.$util.showToast({
title: '商品库存不足'
});
return;
}
try {
this.$pos.send('CloseWeigher');
} catch (e) {
}
}
this.skuInfo.is_adjust = this.skuInfo.adjust_price != this.skuInfo.price;
// 设置右侧商品的调价
if (!this.goodsData.list[this.actionIndex].adjust['sku_id_' + this.skuInfo.sku_id]) {
this.goodsData.list[this.actionIndex].adjust['sku_id_' + this.skuInfo.sku_id] = {};
}
this.goodsData.list[this.actionIndex].adjust['sku_id_' + this.skuInfo.sku_id].adjust_price = this.skuInfo.adjust_price;
this.goodsData.list[this.actionIndex].adjust['sku_id_' + this.skuInfo.sku_id].is_adjust = this.skuInfo.is_adjust;
this.handleSelectGoods(this.skuInfo);
this.scanCode = {
lastTime: 0,
code: ''
};
this.$store.commit('billing/setActive', 'SelectGoodsAfter'); // 记录页面当前活跃值:选择完商品
this.$refs.skuPopup.close();
},
search() {
switch (this.type) {
case 'service':
this.serviceData.page = 0;
this.serviceData.index = 1;
this.$refs.loading.show();
this.getService();
break;
case 'goods':
this.goodsData.page = 0;
this.goodsData.index = 1;
this.$refs.goodsLoading.show();
this.getGoods();
break;
}
},
destroyed() {
this.mediaQueryOb.disconnect();
},
switchStoreAfter() {
this.serviceCategory = [];
this.serviceCategoryId = 'all';
this.serviceData = {
size: 12,
index: 1,
page: 0,
total: 1,
list: []
};
this.goodsCategoryId = 'all';
this.goodsCategory = [];
this.goodsData = {
size: 30,
index: 1,
page: 0,
total: 1,
list: []
};
this.getServiceCategoryFn();
this.getGoodsCategoryFn();
this.getService();
this.getGoods();
},
pageChange(e) {
if (this.type == 'goods') {
this.goodsData.index = e.current;
this.getGoods();
} else if (this.type == 'service') {
this.serviceData.index = e.current;
this.getService();
}
},
switchItem(type) {
this.type = type;
if (this.type == 'goods') {
this.goodsData.index = 1;
this.getGoods();
} else if (this.type == 'service') {
this.serviceData.index = 1;
this.getService();
} else if (this.type == 'money') {
// 无码商品
this.setActive('UnnumberedGoods');
}
},
keydown(value) {
let arr = this.paymentMoney.split('.');
if (arr[1]) {
if (value == '.' || arr[1].length == 2) return;
if (value == '00' && arr[1].length == 1) value = '0';
}
if (parseFloat(this.paymentMoney + value) > 1000000) {
this.$util.showToast({
title: '最大不能超过1000000'
});
return;
}
this.paymentMoney += value;
},
deleteCode() {
this.paymentMoney = this.paymentMoney.substr(0, this.paymentMoney.length - 1);
},
paymentMoneyConfirm() {
if (!this.paymentMoney.length) {
this.$util.showToast({
title: '请输入收款金额'
});
return;
}
if (isNaN(parseFloat(this.paymentMoney)) || !/^(([0-9][0-9]*)|(([0]\.\d{1,2}|[1-9][0-9]*\.\d{1,2})))$/.test(parseFloat(this.paymentMoney))) {
this.$util.showToast({
title: '收款金额格式错误'
});
return;
}
if (this.paymentMoney <= 0) {
this.$util.showToast({
title: '收款金额不能小于等于0'
});
return;
}
if (parseFloat(this.paymentMoney) > 1000000) {
this.$util.showToast({
title: '最大不能超过1000000'
});
return;
}
this.handleSelectGoods({
goods_id: parseInt(new Date().getTime() / 1000),
sku_id: parseInt(new Date().getTime() / 1000),
num: 1,
money: parseFloat(this.paymentMoney)
});
this.scanCode = {
lastTime: 0,
code: ''
};
this.$store.commit('billing/setActive', 'SelectGoodsAfter'); // 记录页面当前活跃值:选择完商品
this.paymentMoney = '';
},
/**
* 打开收银秤
*/
openCashierScale() {
if (this.addon.includes('scale')) {
if (!this.cashierScale) {
getElectronicScaleInformation().then(res => {
if (res.code == 0 && res.data) {
this.cashierScale = res.data.config;
try {
this.$pos.send('OpenWeigher', `OS2X:${this.cashierScale.serialport}:${this.cashierScale.baudrate}`);
} catch (e) {
this.cashierScale = null
}
}
})
} else {
try {
this.$pos.send('OpenWeigher', `OS2X:${this.cashierScale.serialport}:${this.cashierScale.baudrate}`);
} catch (e) {
this.cashierScale = null
}
}
}
},
/**
* 去皮
*/
tare() {
this.$pos.send('Tare')
},
/**
* 清零
*/
zero() {
this.$pos.send('Zero');
},
setActive(key) {
this.$store.commit('billing/setActive', key);
},
paymentMoneyChange(event) {
if (this.paymentMoney.length == 0) {
// 如果没有输入则结账
this.setActive('SelectGoodsAfter')
} else {
this.setActive('UnnumberedGoods')
}
},
// 商品数量减少
dec(data) {
if (this.skuInfo) {
if (this.skuInfo.num <= 1 && this.skuInfo.stock > 0) {
this.skuInfo.num = 1;
} else if(this.skuInfo.stock > 0) {
this.skuInfo.num--;
}
this.$forceUpdate();
}
},
// 商品数量增加
inc(data) {
if (this.skuInfo) {
if (this.skuInfo.num >= this.skuInfo.stock && this.skuInfo.stock > 0) {
this.skuInfo.num = this.skuInfo.stock;
} else if(this.skuInfo.stock > 0){
this.skuInfo.num++;
}
this.$forceUpdate();
}
},
// 打开钱箱
openCashBox() {
this.$emit('openCashBox')
},
setGoodsCategoryShow(id, index) {
if (!this.goodsCategoryShow) {
if (id === 'all') {
if (this.goodsCategory.length > 13) {
this.goodsCategoryShow = !this.goodsCategoryShow
} else {
this.goodsCategoryId = id
}
} else {
this.goodsCategoryId = id
}
} else {
this.goodsCategoryId = id;
this.goodsCategoryIndex = index;
this.goodsCategoryShow = false
}
},
setServiceCategoryShow(id, index) {
if (!this.serviceCategoryShow) {
if (id === 'all') {
if (this.serviceCategory.length > 13) {
this.serviceCategoryShow = !this.serviceCategoryShow
} else {
this.serviceCategoryId = id
}
} else {
this.serviceCategoryId = id
}
} else {
this.serviceCategoryId = id;
this.serviceCategoryIndex = index;
this.serviceCategoryShow = false
}
},
isListShow() {
this.goodsCategoryShow = false;
this.serviceCategoryShow = false;
},
// 获取商品信息通过条形码
getSkuByCode(code) {
if (this.type != 'goods' || this.billingActive == 'ShowMember' || this.billingActive == 'OrderCreate') return;
code = code.toString().trim();
getGoodsInfoByCode(code).then(res => {
if (res.code == 0) {
if (res.data) {
if (res.data.goods_state == 0) {
this.$util.showToast({
title: '该商品已下架'
});
return;
}
if (res.data.stock == 0) {
this.$util.showToast({
title: '该商品库存不足!'
});
return;
}
this.handleSelectGoods(res.data);
this.scanCode = {
lastTime: 0,
code: ''
};
this.$store.commit('billing/setActive', 'SelectGoodsAfter'); // 记录页面当前活跃值:选择完商品
} else {
this.$util.showToast({
title: '未找到该商品!'
})
}
} else {
this.$util.showToast({
title: res.message,
})
}
})
},
// 处理选中商品数据
handleSelectGoods(data) {
let key = 'sku_' + data.sku_id;
let num = data.num || 1;
num = parseInt(num);
// 项目服务和称重商品每次都是新增重新定义key
if (data.goods_class == 4 || data.goods_class == 6) {
if (data.status == 'edit') {
// 编辑
key = data.editKey;
} else {
//新增
var index = 0;
Object.keys(this.billingGoodsData).forEach(k => {
if (k.indexOf(key) != -1) {
index++;
}
});
key += '_' + index;
}
}
// 称重商品,计价方式:计重
if (data.goods_class == 6 && data.pricing_type == 'weight') num = Number.parseFloat(data.weigh);
let _billingGoodsData = this.$util.deepClone(this.billingGoodsData);
// 已加入清单,追加数量
if (_billingGoodsData[key]) {
_billingGoodsData[key].num += num;
} else {
// 第一次加入清单,设置数据
_billingGoodsData[key] = this.$util.deepClone(data);
_billingGoodsData[key].num = num;
}
// 编辑商品数量
if (data.status && data.status == 'edit') {
_billingGoodsData[key].num = num;
}
// 称重商品每次覆盖数量
if (data.goods_class == 6 && data.pricing_type == 'weight') {
_billingGoodsData[key].num = parseFloat(_billingGoodsData[key].num.toFixed(3))
}
// 调整单价
if (data.adjust_price) {
_billingGoodsData[key].price = parseFloat(data.adjust_price);
_billingGoodsData[key].adjust_price = parseFloat(data.adjust_price);
}
_billingGoodsData[key].is_adjust = data.is_adjust || false;
this.$store.commit('billing/setGoodsData', _billingGoodsData);
},
/**
* 添加扫码监听事件
*/
addScanCodeEvent() {
// #ifdef APP-PLUS
plus.key.addEventListener('keyup', this.listenerScanCode, true);
// #endif
// #ifdef H5
window.addEventListener('keypress', this.listenerScanCode, true);
// #endif
},
/**
* 移除扫码监听事件
*/
removeScanCodeEvent() {
// #ifdef APP-PLUS
plus.key.removeEventListener('keyup', this.listenerScanCode, true);
// #endif
// #ifdef H5
window.removeEventListener('keypress', this.listenerScanCode, true);
// #endif
},
/**
* 监听扫码事件
* @param {Object} e
*/
listenerScanCode(e) {
const clearBarCode = () => {
this.scanCode = {
lastTime: 0,
code: ''
};
this.$store.commit('billing/setIsScanTrigger', false);
};
// #ifdef H5
var currCode = e.keyCode || e.which || e.charCode;
// #endif
// #ifdef APP-PLUS
const keyArr = {
'keycode_7': 0,
'keycode_8': 1,
'keycode_9': 2,
'keycode_10': 3,
'keycode_11': 4,
'keycode_12': 5,
'keycode_13': 6,
'keycode_14': 7,
'keycode_15': 8,
'keycode_16': 9
};
var currCode = keyArr['keycode_' + e.keyCode] || '';
// #endif
var currTime = new Date().getTime();
if (this.scanCode.lastTime > 0) {
if (currTime - this.scanCode.lastTime <= 100) {
this.scanCode.code += String.fromCharCode(currCode);
this.$store.commit('billing/setIsScanTrigger', true);
} else if (currTime - this.scanCode.lastTime > 500) {
// 输入间隔500毫秒清空
clearBarCode();
}
} else {
this.scanCode.code = String.fromCharCode(currCode);
}
this.scanCode.lastTime = currTime;
// #ifdef H5
var code = 13;
// #endif
// #ifdef APP-PLUS
var code = 66;
// #endif
if (currCode == code) {
// 扫码枪
if (this.scanCode.code && this.scanCode.code.length >= 8) {
this.scanCodeSearch = true;
this.getSkuByCode(this.scanCode.code);
this.$store.commit('billing/setIsScanTrigger', true);
}
// 回车输入后清空
clearBarCode();
}
},
/**
* 添加键盘监听事件
*/
addKeyDownEvent() {
// #ifdef H5
window.addEventListener("keydown", this.listenerKeyDown, true);
// #endif
},
/**
* 移除键盘监听事件
*/
removeKeyDownEvent() {
// #ifdef H5
window.removeEventListener("keydown", this.listenerKeyDown, true);
// #endif
},
// 监听键盘按下事件
listenerKeyDown(e) {
var code = e.code;
if (this.billingActive == 'SelectGoodsSku') {
// 打开商品规格项弹出框
if (code == 'Enter' || code == 'NumpadEnter') {
this.skuConfirm();
}
} else if (this.billingActive == 'UnnumberedGoods') {
// 无码商品
if (code == 'Enter' || code == 'NumpadEnter') {
if (!this.billingIsScanTrigger) this.paymentMoneyConfirm();
}
}
},
},
};
/**
* 监听重量变化
* @param {Object} text
*/
window.WEIGHER_DATA_CALLBACK = function (text) {
let data = text.split(':');
self.$set(self.skuInfo, 'weigh', data[0] != '-' ? data[0] : '')
};

View File

@@ -0,0 +1,628 @@
.container {
height: 100%;
}
.header-action {
padding: 0.22rem 0.24rem;
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 0.04rem;
margin-bottom: 0.2rem;
height: 0.88rem;
.header-action-left {
display: flex;
align-items: center;
height: 0.44rem;
background-color: var(--primary-color-light-9);
border-radius: 0.22rem;
view {
min-width: 1.02rem;
height: 0.44rem;
line-height: 0.44rem;
text-align: center;
font-size: 0.14rem;
border-left-width: 0;
transition: all 0.3s;
cursor: pointer;
border-radius: 0.22rem;
color: $primary-color;
&.active {
color: #fff;
background-color: $primary-color;
}
}
}
.header-action-center {
margin-left: 0.29rem;
.left {
margin-right: 0.24rem;
}
view {
min-width: 1.02rem;
height: 0.44rem;
line-height: 0.44rem;
text-align: center;
font-size: 0.14rem;
border-left-width: 0;
transition: all 0.3s;
cursor: pointer;
border-radius: 0.22rem;
color: #fff;
background-color: $primary-color;
}
}
.header-action-right {
display: flex;
align-items: center;
height: 0.4rem;
border-radius: 0.2rem;
border: 0.01rem solid #CCCCCC;
overflow: hidden;
box-sizing: border-box;
input {
background: #ffffff;
border-radius: 0.02rem;
height: 0.36rem;
padding-left: 0.24rem;
font-size: 0.14rem;
width: 2.80rem;
box-sizing: border-box;
}
.search-btn {
width: 0.36rem;
height: 0.36rem;
background: #ffffff;
border-radius: 0.02rem;
border-left: 0;
text-align: center;
line-height: 0.36rem;
cursor: pointer;
}
}
}
.content {
position: relative;
box-sizing: border-box;
height: calc(100% - 1.08rem);
transform: rotate(0);
display: flex;
.table-pages {
display: flex;
justify-content: flex-end;
position: absolute;
bottom: 0;
left: 1.72rem;
right: 0;
background-color: #fff;
padding: 0.24rem 0.20rem;
z-index: 1;
}
}
.type-switch {
width: 1.52rem;
margin-right: 0.2rem;
padding: 0.24rem 0;
font-size: 0.14rem;
white-space: nowrap;
box-sizing: border-box;
border-radius: 0.04rem;
position: relative;
.list {
height: calc(100% - 0.6rem) !important;
}
.list-all {
position: absolute;
width: 0;
height: 100%;
left: 0;
top: 0;
z-index: 99;
box-shadow: 0.04rem 0 0.12rem 0 rgba(0, 0, 0, 0.04);
transition: width 0.3s;
box-sizing: border-box;
overflow: hidden;
border-radius: 0.04rem;
&.show {
width: 4.08rem;
height: 100%;
}
.center {
width: 4.08rem;
height: 100%;
padding: 0.24rem;
overflow-y: auto;
flex-wrap: wrap;
.switch-item {
margin-right: 0.24rem;
width: 1.04rem;
&:nth-child(2n+2){
margin-right: 0;
}
}
}
}
/deep/ .uni-scroll-view-content {
display: flex;
flex-direction: column;
// align-items: center;
}
// /deep/ .uni-scroll-view {
// width: 1.1rem !important;
// }
.switch-item {
width: 1.04rem;
border-radius: 0.04rem;
padding: 0.1rem 0.1rem;
font-size: 0.14rem;
text-align: center;
margin-left: 0.24rem;
cursor: pointer;
box-sizing: border-box;
border: 0.01rem solid #DCDEE2;
.item-title{
white-space: normal;
max-height: 0.4rem;
overflow: hidden;
word-break: break-all;
}
}
.all-category{
.switch-item {
height: 0.4rem;
width: 1.04rem;
border-radius: 0.04rem;
padding: 0 0.1rem;
font-size: 0.14rem;
text-align: center;
line-height: 0.4rem;
margin-left: 0.24rem;
cursor: pointer;
overflow: hidden;
box-sizing: border-box;
border: 0.01rem solid #DCDEE2;
}
}
.switch-item.active {
background-color: $primary-color;
border-color: $primary-color;
color: #fff;
}
.switch-item:not(:last-child) {
margin-bottom: 0.20rem;
}
}
.list-wrap {
position: relative;
flex: 1;
padding-bottom: 0.78rem;
}
.money-pages {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
.money-wrap {
background: #fff;
border-radius: 0.05rem;
padding-top: 0.3rem;
.head {
height: 0.6rem;
line-height: 0.6rem;
text-align: center;
font-weight: bold;
position: relative;
text {
font-size: 0.16rem;
}
.iconguanbi1 {
position: absolute;
right: 0.15rem;
font-size: 0.22rem;
cursor: pointer;
}
}
.content-wrap {
display: flex;
border: 0.01rem solid #e6e6e6;
height: 0.6rem;
align-items: center;
margin: 0 0.2rem;
padding: 0 0.15rem;
.unit {
font-size: 0.25rem;
}
.money {
margin-left: 0.05rem;
font-size: 0.25rem;
}
}
.keyboard-wrap {
width: 4rem;
padding: 0 0.2rem 0.3rem 0.2rem;
margin-top: 0.1rem;
}
}
}
.table-list {
display: flex;
align-items: center;
flex-wrap: wrap;
width: calc(100% + 0.12rem);
.table-item {
border: 0.01rem solid #fff;
box-sizing: border-box;
padding: 0.1rem 0.18rem 0.1rem 0.1rem;
background-color: #fff;
margin-bottom: 0.12rem;
margin-right: 0.12rem;
cursor: pointer;
transition: border-color, background-color 0.3s;
position: relative;
border-radius: 0.04rem;
&.item-mum-2{
width: calc((100% - 0.25rem) / 2);
}
&.item-mum-3{
width: calc((100% - 0.37rem) / 3);
}
&.item-mum-4{
width: calc((100% - 0.49rem) / 4);
}
&:focus{
outline: none;
}
.item-other {
display: flex;
flex-wrap: wrap;
margin-left: 0.1rem;
}
.item-img {
width: 0.9rem;
height: 0.9rem;
display: flex;
align-items: center;
overflow: hidden;
-ms-flex-negative: 0;
-webkit-flex-shrink: 0;
flex-shrink: 0;
image {
width: 100%;
border-radius: 0.03rem;
}
}
.item-name {
height: 0.4rem;
line-height: 0.2rem;
max-width: 1.58rem;
margin-bottom: 0.05rem;
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.item-info {
cursor: pointer;
flex: 1;
display: flex;
.item-time {
font-size: 0.12rem;
color: #909399;
}
.item-money {
font-size: 0.14rem;
color: $primary-color;
height: 0.19rem;
line-height: 0.19rem;
}
.item-stock {
height: 0.17rem;
font-size: 0.12rem;
color: #808695;
line-height: 0.17rem;
}
}
.no-stock {
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.5);
cursor: not-allowed;
image {
height: 60%;
}
}
}
.table-item.yes-stock {
&:hover {
background-color: var(--primary-color-light-9);
border-color: $primary-color;
}
&.focus,
&:focus {
background-color: var(--primary-color-light-9);
border-color: $primary-color;
outline: none;
}
&.active {
border-color: $primary-color;
background-color: $primary-color;
color: #fff;
.item-time,
.item-money,
.item-stock {
color: #fff;
}
}
}
}
.sku-wrap {
display: flex;
flex-direction: column;
width: 7rem;
height: 4rem;
background-color: #fff;
border-radius: 0.04rem;
box-shadow: 0 0.01rem 0.12rem 0 rgba(0, 0, 0, 0.1);
.header,
.footer {
height: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 0.01rem solid #e6e6e6;
position: relative;
.title {
font-size: 0.16rem;
font-weight: bold;
}
.iconfont {
line-height: 1;
position: absolute;
right: 0.15rem;
font-size: 0.2rem;
cursor: pointer;
}
}
.body {
flex: 1;
padding: 0.15rem;
scroll-view {
height: 2.55rem;
}
}
.spec {
line-height: 0.35rem;
font-weight: bold;
margin-top: 0.1rem;
}
.spec-value {
.value-item {
display: inline-block;
height: 0.3rem;
line-height: 0.3rem;
background-color: #fff;
color: #333;
border: 0.01rem solid #e6e6e6;
margin: 0.05rem 0.1rem 0 0;
padding: 0 0.1rem;
cursor: pointer;
transition: all 0.3s;
border-radius: 0.04rem;
&:hover,
&.active {
background: var(--primary-color-light-9);
border-color: $primary-color;
color: $primary-color;
}
&.disabled {
color: #999;
cursor: not-allowed;
background: #f7f7f7;
border-color: #f7f7f7;
}
}
}
.spec-value-form {
display: flex;
align-items: center;
.spec-value-input {
margin-right: 0.05rem;
border-width: 0.01rem;
border-style: solid;
background-color: #fff;
color: rgba(0, 0, 0, 0.85);
padding-left: 0.1rem;
height: 0.38rem;
line-height: 0.38rem;
font-size: 0.14rem;
border-color: #e6e6e6;
border-radius: 0.02rem;
width: 2rem;
}
.num-dec {
width: 0.25rem;
height: 0.25rem;
background: #e6e6e6;
border: 0.01rem solid #e6e6e6;
border-radius: 30%;
text-align: center;
line-height: 0.23rem;
font-size: 0.25rem;
margin-right: 0.1rem;
cursor: pointer;
transition: 0.3s;
}
.num-inc {
width: 0.25rem;
height: 0.25rem;
background: $primary-color;
border: 0.01rem solid #e6e6e6;
border-radius: 30%;
text-align: center;
line-height: 0.23rem;
font-size: 0.25rem;
margin-left: 0.1rem;
cursor: pointer;
transition: 0.3s;
color: #fff;
}
}
.scale-action {
button {
width: .8rem;
margin: .15rem .15rem 0 0;
}
}
.goods-info {
display: flex;
.image {
width: 0.7rem;
height: 0.7rem;
margin-right: 0.1rem;
flex-shrink: 0;
overflow: hidden;
image {
width: 100%;
}
}
.info {
display: flex;
flex-direction: column;
justify-content: space-between;
.price {
line-height: 1;
margin-top: 0.05rem;
}
}
}
.footer {
border: 0;
height: 0.6rem;
button {
width: 95%;
}
}
}
.empty {
text-align: center;
padding-top: 1.2rem;
image {
width: 2rem;
}
.tips {
color: #999;
margin-top: 0.15rem;
}
}
@media screen and (min-width: 450px) and (max-width: 950px) {
.type-switch .list-all.show {
width: 2.78rem;
.center{
width: 2.78rem;
.switch-item:nth-child(2n+2) {
margin-right: 0;
}
.switch-item:nth-child(3n+3) {
margin-right: 0.24rem;
}
}
}
}
@media screen and (min-width: 1201px) and (max-width: 1900px) {
.type-switch .list-all.show {
width: 6.6rem;
.center{
width: 6.6rem;
.switch-item:nth-child(5n+5) {
margin-right: 0;
}
.switch-item:nth-child(3n+3) {
margin-right: 0.24rem;
}
}
}
}

View File

@@ -0,0 +1,239 @@
<template>
<view class="container goods-container">
<view class="header-action common-wrap">
<view class="flex items-center">
<view class="header-action-left">
<view class="flex-1" :class="{ active: type == 'goods' }" @click="switchItem('goods')">商品</view>
<view class="flex-1" :class="{ active: type == 'service' }" @click="switchItem('service')">项目</view>
<view class="flex-1" :class="{ active: type == 'money' }" @click="switchItem('money')">无码商品</view>
</view>
<view class="header-action-center flex" v-if="billingIsShowCashBox">
<view class="flex-1" @click="openCashBox()">打开钱箱</view>
</view>
</view>
<view class="header-action-right">
<input placeholder="请输入商品/项目名称/编码" placeholder-style="font-size:0.14rem;color:#ACACAC;" v-model="searchText" @focus="setActive('inputSearchText')" @blur="setActive('')" @confirm="search" />
<view class="search-btn" @click="search">
<text class="iconfont icon31sousuo"></text>
</view>
</view>
</view>
<view class="content">
<view class="type-switch common-wrap flex-shrink-0 flex uni-column" v-if="serviceCategory.length && type == 'service'">
<view class="switch-item flex-shrink-0" :class="{ active: serviceCategoryId == 'all' }" @click.stop="setServiceCategoryShow('all',0)">所有分类</view>
<scroll-view scroll-y="true" :show-scrollbar="false" :scroll-into-view="'serviceCategory-' + serviceCategoryIndex" class="list flex-shrink-0 common-scrollbar">
<view :id="'serviceCategory-' + index" class="switch-item flex-shrink-0" :class="{ active: serviceCategoryId == item.category_id }" v-for="(item, index) in serviceCategory" :key="index" @click.stop="setServiceCategoryShow(item.category_id,index)">
<view class="item-title">{{ item.category_name }}</view>
</view>
</scroll-view>
<view v-if="serviceCategory.length > 13" class="list-all common-wrap" :class="{ 'show': serviceCategoryShow }" @click.stop="() => { return false }">
<view class="all-category center flex content-start">
<view class="switch-item flex-shrink-0" :class="{ active: serviceCategoryId == 'all' }" @click="setServiceCategoryShow('all', 0)">所有分类</view>
<view class="switch-item flex-shrink-0" :class="{ active: serviceCategoryId == item.category_id }" v-for="(item, index) in serviceCategory" :key="index" @click="setServiceCategoryShow(item.category_id, index)">
<view class="item-title">{{ item.category_name }}</view>
</view>
</view>
</view>
</view>
<view class="type-switch common-wrap flex-shrink-0 flex uni-column" v-if="goodsCategory.length && type == 'goods'">
<view class="switch-item flex-shrink-0" :class="{ active: goodsCategoryId == 'all' }" @click.stop.prevent="setGoodsCategoryShow('all',0)">所有分类</view>
<scroll-view scroll-y="true" :show-scrollbar="false" :scroll-into-view="'goodsCategory-' + goodsCategoryIndex" class="list flex-shrink-0 common-scrollbar" v-show="goodsCategory.length && type == 'goods'">
<view :id="'goodsCategory-' + index" class="switch-item flex-shrink-0" :class="{ active: goodsCategoryId == item.category_id }" v-for="(item, index) in goodsCategory" :key="index" @click.stop="setGoodsCategoryShow(item.category_id,index)">
<view class="item-title">{{ item.category_name }}</view>
</view>
</scroll-view>
<view v-if="goodsCategory.length > 13" class="list-all common-wrap" :class="{ 'show': goodsCategoryShow }" @click.stop="() => { return false }">
<view class="all-category center flex content-start">
<view class="switch-item flex-shrink-0" :class="{ active: goodsCategoryId == 'all' }" @click="setGoodsCategoryShow('all')">所有分类</view>
<view class="switch-item flex-shrink-0" :class="{ active: goodsCategoryId == item.category_id }" v-for="(item, index) in goodsCategory" :key="index" @click="setGoodsCategoryShow(item.category_id, index)">
<view class="item-title">{{ item.category_name }}</view>
</view>
</view>
</view>
</view>
<scroll-view scroll-y="true" class="list-wrap goods" v-show="type == 'goods'" @scrolltolower="nextGoodsLiist">
<view class="table-list" v-show="goodsData.list.length" data-focus="true">
<view class="table-item goods-select-focus goods-focus"
:class="{ 'yes-stock': item.stock>0, 'item-mum-2': itemNum == 2, 'item-mum-3': itemNum == 3, 'item-mum-4': itemNum == 4, active: billingGoodsIds.indexOf(item.goods_id) != -1, focus: indexFocus == index }"
v-for="(item, index) in goodsData.list" :key="index" @click="goodsSelect(item, index)" tabindex="0" :data-tab-index="index">
<view class="item-info">
<view class="item-img">
<image v-if="item.goods_image == '@/static/goods/goods.png'" src="@/static/goods/goods.png" mode="widthFix"/>
<image v-else :src="$util.img(item.goods_image.split(',')[0], { size: 'small' })" @error="item.goods_image = '@/static/goods/goods.png'" mode="widthFix"/>
</view>
<view class="item-other flex-1">
<view class="item-name multi-hidden">{{ item.goods_name }}</view>
<view class="w-full flex justify-between items-center self-end">
<view class="item-money">{{ item.price | moneyFormat }}</view>
<view class="item-stock">库存{{ item.stock }}</view>
</view>
</view>
</view>
<view class="no-stock" v-if="item.stock <= 0">
<image src="@/static/stock/stock_empty.png" mode="heightFix"/>
</view>
</view>
</view>
<view class="empty" v-show="isGoodsLoad && !goodsData.list.length">
<image src="@/static/goods/goods_empty.png" mode="widthFix"/>
<view class="tips">暂无商品</view>
</view>
<ns-loading :layer-background="{ background: 'rgba(255,255,255,.6)' }" :default-show="false" ref="goodsLoading"></ns-loading>
</scroll-view>
<scroll-view scroll-y="true" class="list-wrap service" v-show="type == 'service'">
<view class="table-list" v-show="serviceData.list.length">
<view class="table-item goods-select-focus service-focus"
:class="{ 'yes-stock': item.stock > 0, 'item-mum-2': itemNum == 2, 'item-mum-3': itemNum == 3, 'item-mum-4': itemNum == 4, active: billingGoodsIds.indexOf(item.goods_id) != -1, focus: indexFocus == index }"
v-for="(item, index) in serviceData.list" :key="index" @click="goodsSelect(item, index)" tabindex="0" :data-tab-index="index">
<view class="item-info">
<view class="item-img">
<image v-if="item.goods_image == '@/static/goods/goods.png'" src="@/static/goods/goods.png" mode="widthFix"/>
<image v-else :src="$util.img(item.goods_image.split(',')[0], { size: 'small' })" @error="item.goods_image = '@/static/goods/goods.png'" mode="widthFix"/>
</view>
<view class="item-other">
<view class="item-name multi-hidden">{{ item.goods_name }}</view>
<view class="w-full flex justify-between items-center self-end">
<view class="item-money">{{ item.price | moneyFormat }}</view>
<view class="item-time" v-if="item.service_length">时长{{ item.service_length }}分钟</view>
</view>
</view>
</view>
<view class="no-stock" v-if="item.stock <= 0">
<image src="@/static/stock/stock_empty.png" mode="heightFix"/>
</view>
</view>
</view>
<view class="empty" v-show="!serviceData.list.length">
<image src="@/static/goods/goods_empty.png" mode="widthFix"/>
<view class="tips">暂无商品</view>
</view>
<ns-loading :layer-background="{ background: 'rgba(255,255,255,.6)' }" :default-show="false" ref="loading"></ns-loading>
</scroll-view>
<view class="table-pages" v-show="type == 'service' && serviceData.list.length">
<uni-pagination :total="serviceData.total" :showIcon="true" @change="pageChange" :pageSize="serviceData.size" :value="serviceData.index" />
</view>
<!-- <view class="table-pages" v-show="type == 'goods' && goodsData.list.length">
<uni-pagination :total="goodsData.total" :showIcon="true" @change="pageChange" :pageSize="goodsData.size" :value="goodsData.index" />
</view> -->
<view class="money-pages" v-show="type == 'money'">
<view class="money-wrap">
<view class="content-wrap">
<view class="unit"></view>
<input type="text" class="money" v-model="paymentMoney" @input="paymentMoneyChange" />
</view>
<view class="keyboard-wrap">
<view class="num-wrap">
<view class="key-item" @click="keydown('1')">1</view>
<view class="key-item" @click="keydown('2')">2</view>
<view class="key-item" @click="keydown('3')">3</view>
<view class="key-item" @click="keydown('4')">4</view>
<view class="key-item" @click="keydown('5')">5</view>
<view class="key-item" @click="keydown('6')">6</view>
<view class="key-item" @click="keydown('7')">7</view>
<view class="key-item" @click="keydown('8')">8</view>
<view class="key-item" @click="keydown('9')">9</view>
<view class="key-item" @click="keydown('00')">00</view>
<view class="key-item" @click="keydown('0')">0</view>
<view class="key-item" @click="keydown('.')">.</view>
</view>
<view class="action-wrap">
<view class="delete" @click="deleteCode">删除</view>
<view class="delete" @click="paymentMoney = ''">清空</view>
<view class="confirm" @click="paymentMoneyConfirm">确认</view>
</view>
</view>
</view>
</view>
</view>
<uni-popup ref="skuPopup" type="center">
<view class="sku-wrap">
<view class="header">
<text class="title">{{ skuInfo && skuInfo.status == 'edit' ? '调整' : '选择' }}</text>
<text class="iconfont iconguanbi1" @click="$refs.skuPopup.close()"></text>
</view>
<view class="body">
<scroll-view scroll-y="true">
<view class="goods-info" v-if="skuInfo">
<view class="image">
<image v-if="skuInfo.sku_image == '@/static/goods/goods.png'" src="@/static/goods/goods.png" mode="widthFix"/>
<image v-else :src="$util.img(skuInfo.sku_image, { size: 'small' })" @error="skuInfo.sku_image = '@/static/goods/goods.png'" mode="widthFix"/>
</view>
<view class="info">
<view class="multi-hidden">{{ skuInfo.goods_name }}</view>
<view class="price">{{ skuInfo.adjust_price }} / {{ skuInfo.unit ? skuInfo.unit : '件' }}</view>
<view>库存{{ skuInfo.stock }}</view>
</view>
</view>
<view v-for="(item, index) in goodsSpec" :key="index">
<view class="spec">{{ item.spec_name }}</view>
<view class="spec-value">
<view class="value-item" :class="{ active: spec.selected, disabled: (!spec.selected && skuInfo.status == 'edit') }" v-for="(spec, sindex) in item.value" :key="sindex" @click="skuSelect(spec.sku_id)">
{{ spec.spec_value_name }}
</view>
</view>
</view>
<block v-if="skuInfo">
<view class="spec">单价</view>
<view class="spec-value spec-value-form">
<input type="text" class="spec-value-input" v-model="skuInfo.adjust_price" placeholder="请输入单价" />
<text></text>
</view>
<block v-if="skuInfo.goods_class != 6">
<view class="spec">数量</view>
<view class="spec-value spec-value-form">
<view class="num-dec" @click.stop="dec">-</view>
<input type="text" class="spec-value-input" v-model="skuInfo.num" placeholder="请输入数量" :focus="inputFocus" @focus="inputFocus = true" @blur="inputFocus = false" />
<view class="num-inc" @click.stop="inc">+</view>
<text>{{ skuInfo.unit }}</text>
</view>
</block>
<block v-if="skuInfo.goods_class == 6 && skuInfo.pricing_type == 'weight'">
<view class="info">
<view class="spec">剩余库存</view>
<view>{{ skuInfo.stock }}<text>{{ skuInfo.unit }}</text></view>
</view>
<view>
<view class="spec">称重</view>
<view class="spec-value spec-value-form">
<input type="text" class="spec-value-input" v-model="skuInfo.weigh" placeholder="请输入重量" :focus="inputFocus" @focus="inputFocus = true" @blur="inputFocus = false" />
<text>{{ skuInfo.unit }}</text>
</view>
</view>
<view class="flex scale-action" v-if="addon.includes('scale') && cashierScale">
<button type="primary" class="default-btn" plain @click="zero">归零</button>
<button type="primary" class="default-btn" plain @click="tare">去皮</button>
</view>
</block>
</block>
</scroll-view>
</view>
<view class="footer">
<button type="default" class="primary-btn" @click="skuConfirm" :disabled="skuInfo && skuInfo.stock <= 0">确认</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import index from './index.js';
export default {
mixins: [index],
components:{
}
};
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

View File

@@ -0,0 +1,116 @@
<template>
<view class="loading-layer" v-if="isShow" :style="layerBackground">
<view class="loading-anim">
<view class="box item"><view class="border out item color-base-border-top color-base-border-left"></view></view>
</view>
</view>
</template>
<script>
export default {
name: 'nsLoading',
props: {
layerBackground: {
type: Object,
default() {
return {};
}
},
defaultShow: {
type: Boolean,
default: true
}
},
data() {
return {
isShow: true
};
},
created() {
this.isShow = this.defaultShow;
},
methods: {
show() {
this.isShow = true;
},
hide() {
this.isShow = false;
}
}
};
</script>
<style lang="scss" scoped>
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.loading-layer {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 997;
background: #fff;
}
.loading-anim {
position: absolute;
left: 50%;
top: 40%;
transform: translate(-50%, -50%);
}
.loading-anim > .item {
position: relative;
width: 0.3rem;
height: 0.3rem;
perspective: 8rem;
transform-style: preserve-3d;
transition: all 0.2s ease-out;
}
.loading-anim .border {
position: absolute;
border-radius: 50%;
border: 0.03rem solid $primary-color;
}
.loading-anim .out {
top: 15%;
left: 15%;
width: 70%;
height: 70%;
// border-left-color: red !important;
border-right-color: rgba($color: #000000, $alpha: 0) !important;
// border-top-color: rgba($color: #000000, $alpha: 0) !important;
border-bottom-color: rgba($color: #000000, $alpha: 0) !important;
animation: spin 0.6s linear normal infinite;
}
.loading-anim .in {
top: 25%;
left: 25%;
width: 50%;
height: 50%;
border-top-color: transparent !important;
border-bottom-color: transparent !important;
animation: spin 0.8s linear infinite;
}
.loading-anim .mid {
top: 40%;
left: 40%;
width: 20%;
height: 20%;
border-left-color: transparent;
border-right-color: transparent;
animation: spin 0.6s linear infinite;
}
</style>

View File

@@ -0,0 +1,195 @@
import {
getMemberCardList
} from '@/api/member'
import {mapGetters} from 'vuex';
export default {
data() {
return {
memberCardData: {
page: 0,
total: 1,
list: [],
index: 0,
currData: {},
selected: {}
},
itemNum: 1
}
},
computed: {
...mapGetters(['billingGoodsData'])
},
methods: {
open() {
this.$refs.memberCardPopup.open();
this.memberCardData.page = 0;
this.memberCardData.index = 0;
this.memberCardData.list = [];
this.memberCardData.currData = {};
this.memberCardData.selected = {};
this.getMemberCard()
},
// 获取会员项目
getMemberCard() {
if (this.memberCardData.page + 1 > this.memberCardData.total) return;
this.memberCardData.page += 1;
getMemberCardList({
status: 1,
page: this.memberCardData.page,
member_id: this.globalMemberInfo.member_id
}).then(res => {
if (res.code == 0) {
this.memberCardData.total = res.data.page_count || 1;
Object.values(this.billingGoodsData).forEach(data => {
if (data.card_id) {
res.data.list.forEach((card)=>{
if(card.card_id == data.card_id){
// 通用卡:选择商品,总数量发生变化
if (data.card_type == 'commoncard') {
card.total_use_num += data.num;
} else if (data.card_type == 'oncecard') {
// 限次卡:选择商品后,商品和总数量都要发生变化
card.total_use_num += data.num;
card.item_list.forEach((card_item)=>{
if(card_item.item_id == data.item_id){
card_item.use_num += data.num;
}
})
}
}
})
}
});
if (res.data.list.length) this.memberCardData.list = this.memberCardData.list.concat(res.data.list);
if (this.memberCardData.page == 1) {
// 默认展示第一个卡项信息
if (res.data.count) this.selectMemberCard(this.memberCardData.list[0], 0);
}
}
})
},
/**
* 选择会员套餐
* @param {Object} data
* @param {Object} index
*/
selectMemberCard(data, index) {
this.memberCardData.index = index;
this.memberCardData.currData = this.$util.deepClone(data);
this.memberCardData.selected = {};
},
/**
* 选择会员套餐商品项
* @param {Object} data
* @param {Object} index
*/
selectMemberCardItem(data, index) {
if (this.memberCardData.selected['item_' + data.item_id]) {
if (data.card_type == 'commoncard') {
this.memberCardData.currData.total_use_num -= this.memberCardData.selected['item_' + data.item_id].input_num;
}
delete this.memberCardData.selected['item_' + data.item_id];
} else {
if (!this.checkStatus(data)) return;
this.memberCardData.selected['item_' + data.item_id] = this.$util.deepClone(data);
this.memberCardData.selected['item_' + data.item_id].input_num = 1;
this.memberCardData.selected['item_' + data.item_id].index = index;
this.memberCardData.selected['item_' + data.item_id].card_name = this.memberCardData.currData.goods_name;
if (data.card_type == 'commoncard') {
this.memberCardData.currData.total_use_num += 1;
}
}
this.$forceUpdate();
},
/**
* 加入购物车
*/
selectGoods() {
if (!Object.keys(this.memberCardData.selected).length) {
this.$util.showToast({
title: '请选择服务/商品',
});
return;
}
let billingGoodsData = this.$util.deepClone(this.billingGoodsData);
let billingGoodsKeys = Object.keys(billingGoodsData);
Object.keys(this.memberCardData.selected).forEach((key) => {
let data = this.memberCardData.selected[key];
data.card_index = this.memberCardData.index;
this.memberCardData.list[this.memberCardData.index].total_use_num += data.input_num;
this.memberCardData.list[this.memberCardData.index].item_list[data.index].use_num += data.input_num;
this.memberCardData.currData.item_list[data.index].use_num += data.input_num;
//服务商品每个都是一个订单项,需要循环处理
if(data.goods_class == this.$util.goodsClassDict.service){
let addNum = 0;
Object.values(billingGoodsData).forEach((item)=>{
if(item.sku_id == data.sku_id){
addNum ++;
}
})
data.num = 1;
for(let num = 1;num <= data.input_num;num ++){
let skuKey = 'sku_' + data.sku_id + '_item_' + data.item_id + '_' + addNum;
billingGoodsData[skuKey] = this.$util.deepClone(data);
addNum ++;
}
}else{
data.num = data.input_num;
let skuKey = 'sku_' + data.sku_id + '_item_' + data.item_id;
if(billingGoodsData.hasOwnProperty(skuKey)){
data.num += billingGoodsData[skuKey].num;
}
billingGoodsData[skuKey] = this.$util.deepClone(data);
}
});
this.$store.commit('billing/setGoodsData', billingGoodsData);
this.memberCardData.selected = {};
},
/**
* 数量减
* @param {Object} data
*/
itemDec(data) {
let currData = this.memberCardData.currData;
if (this.memberCardData.selected['item_' + data.item_id].input_num > 1) {
this.memberCardData.selected['item_' + data.item_id].input_num -= 1;
if (data.card_type == 'commoncard') {
currData.total_use_num -= 1;
}
this.$forceUpdate();
}
},
/**
* 数量加
* @param {Object} data
*/
itemInc(data) {
let currData = this.memberCardData.currData;
if (data.card_type == 'commoncard') {
if ((currData.total_num - currData.total_use_num - 1) < 0) return;
} else if (data.card_type == 'oncecard') {
if ((data.num - data.use_num - this.memberCardData.selected['item_' + data.item_id].input_num - 1) < 0) return;
}
if (data.card_type == 'commoncard') {
currData.total_use_num += 1;
}
this.memberCardData.selected['item_' + data.item_id].input_num += 1;
this.$forceUpdate();
},
checkStatus(data) {
let currData = this.memberCardData.currData;
if (data.card_type == 'commoncard') {
return currData.total_num > currData.total_use_num;
} else if (data.card_type == 'oncecard') {
return data.num > data.use_num;
}
return true;
}
}
}

View File

@@ -0,0 +1,376 @@
.container {
height: 100%;
}
.header {
height: 0.66rem;
line-height: 0.66rem;
text-align: left;
border-bottom: 0.01rem solid #e6e6e6;
color: #303133;
font-size: 0.14rem;
}
.info-wrap {
display: flex;
flex-direction: column;
height: 6.5rem;
padding: 0 0.2rem;
box-sizing: border-box;
.headimg-content {
display: flex;
align-items: center;
margin-top: 0.2rem;
.headimg {
width: 0.7rem;
height: 0.7rem;
border-radius: 50%;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
.header-info {
margin-left: 0.15rem;
width: calc(100% - 0.85rem);
.name {
font-size: 0.16rem;
color: #303133;
text {
background: #ffffff;
border: 0.01rem solid $primary-color;
border-radius: 0.02rem;
font-size: 0.12rem;
color: $primary-color;
margin-left: 0.15rem;
padding: 0.01rem 0.04rem;
}
}
.header-info-item {
display: flex;
align-items: center;
margin-top: 0.1rem;
justify-content: space-between;
view {
text-align: left;
font-size: 0.14rem;
color: #303133;
opacity: 0.9;
}
}
}
}
}
.empty {
text-align: center;
padding-top: 1.2rem;
margin: 0 auto;
image {
width: 2rem;
}
.tips {
color: #999;
margin-top: 0.15rem;
}
}
.member-card-wrap {
flex: 1;
height: 0;
display: flex;
flex-direction: column;
.card-wrap {
flex: 1;
height: 0;
display: flex;
padding-top: 0.2rem;
margin-bottom: 0.2rem;
}
.card-list {
width: 2rem;
border: 0.01rem solid #e6e6e6;
margin-right: 0.1rem;
padding: 0.1rem 0;
.card-item {
width: calc(100% - 0.2rem);
height: 1rem;
border: 0.01rem solid $primary-color;
margin: 0 0.1rem 0.1rem 0.1rem;
box-sizing: border-box;
border-radius: 0.05rem;
cursor: pointer;
padding: 0.15rem 0.1rem;
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: var(--primary-color-light-8);
&.active {
background-color: $primary-color;
color: #fff;
.card-name {
color: #fff;
}
.info {
color: #fff;
}
}
.card-name {
font-weight: bold;
}
.info {
display: flex;
justify-content: space-between;
color: #999;
& > view {
font-size: 0.12rem;
}
}
}
}
.item-list {
flex: 1;
border: 0.01rem solid #e6e6e6;
display: flex;
flex-direction: column;
width: 0;
.content {
padding: 0 0.1rem;
}
.empty {
padding-top: 0.8rem;
}
.title {
line-height: 0.3rem;
padding: 0.1rem;
display: flex;
justify-content: space-between;
.num {
color: $primary-color;
margin: 0 0.02rem;
}
}
.button-wrap {
display: flex;
background-color: #fff;
height: 0.5rem;
line-height: 0.5rem;
align-items: center;
justify-content: flex-end;
box-shadow: 0 0.04rem 0.12rem 0 rgba(0, 0, 0, 0.1);
padding: 0.1rem 0;
button {
height: 0.4rem;
line-height: 0.4rem;
margin: 0 0.1rem 0 0;
}
}
.item-wrap {
flex: 1;
height: 0;
display: flex;
.uni-flex {
flex-wrap: wrap;
}
}
.card-item {
display: flex;
width: calc(50% - 0.05rem);
padding: 0.1rem;
border: 0.01rem solid #eee;
border-radius: 0.03rem;
cursor: pointer;
transition: all 0.3s;
box-sizing: border-box;
margin-bottom: 0.1rem;
.image {
width: 0.7rem;
height: 0.7rem;
margin-right: 0.1rem;
overflow: hidden;
image {
width: 100%;
}
}
.info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
width: 0;
.num {
margin-top: 0.05rem;
color: #999;
font-size: 0.12rem;
}
.price {
font-size: 0.14rem;
color: #fe2278;
line-height: 1;
.util {
font-size: 0.12rem;
}
}
.name {
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
line-height: 1.5;
.tag {
border-radius: 0.02rem;
padding: 0.01rem 0.05rem;
background-color: var(--primary-color-light-8);
color: $primary-color;
font-size: 0.12rem;
margin-right: 0.05rem;
}
}
}
}
.action-wrap {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.12rem;
margin-top: 0.05rem;
height: 0.25rem;
}
.number-wrap {
display: none;
height: 0.25rem;
border: 0.01rem solid #e6e6e6;
border-radius: 0.02rem;
overflow: hidden;
input {
height: 0.25rem;
line-height: 0.25rem;
width: 0.25rem;
border: 1px solid #e6e6e6;
text-align: center;
background: #fff;
font-size: 0.12rem;
}
.iconfont {
height: 0.25rem;
width: 0.25rem;
text-align: center;
line-height: 0.25rem;
background: #f5f5f5;
}
}
.card-item.active {
background-color: var(--primary-color-light-2);
.num {
color: #fff;
}
.price {
color: #fff;
}
.name {
color: #fff;
.tag {
background-color: #fff;
}
}
.number-wrap {
display: flex;
}
}
.not-select {
background: #eee;
cursor: not-allowed;
}
}
}
// pop弹框
.pop-box {
background: #ffffff;
width: 8rem;
height: 7rem;
.pop-header {
padding: 0 0.15rem 0 0.2rem;
height: 0.5rem;
line-height: 0.5rem;
border-bottom: 0.01rem solid #f0f0f0;
font-size: 0.14rem;
color: #333;
overflow: hidden;
border-radius: 0.02rem 0.2rem 0 0;
box-sizing: border-box;
display: flex;
justify-content: space-between;
.pop-header-text {
}
.pop-header-close {
cursor: pointer;
text {
font-size: 0.18rem;
}
}
}
.pop-content {
height: calc(100% - 1rem);
overflow-y: scroll;
padding: 0.1rem 0.2rem;
box-sizing: border-box;
}
}

View File

@@ -0,0 +1,129 @@
<template>
<view class="container">
<uni-popup ref="memberCardPopup">
<view class="pop-box member-info-wrap">
<view class="pop-header">
<view class="pop-header-text">会员卡项</view>
<view class="pop-header-close" @click="$refs.memberCardPopup.close()">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<view class="info-wrap" v-if="globalMemberInfo">
<view class="headimg-content">
<view class="headimg">
<image :src="globalMemberInfo.headimg ? $util.img(globalMemberInfo.headimg) : $util.img(defaultImg.head)" @error="globalMemberInfo.headimg = defaultImg.head"/>
</view>
<view class="header-info">
<view class="name">
{{ globalMemberInfo.nickname }}
<text v-if="globalMemberInfo.member_level">{{ globalMemberInfo.member_level_name }}</text>
</view>
<view class="header-info-item">
<view>电话{{ globalMemberInfo.mobile }}</view>
<view>性别{{ globalMemberInfo.sex == 0 ? '未知' : globalMemberInfo.sex == 1 ? '男' : '女' }}</view>
<view>生日{{ globalMemberInfo.birthday }}</view>
<view>注册时间{{ globalMemberInfo.reg_time | timeFormat }}</view>
</view>
</view>
</view>
<view class="member-card-wrap">
<view class="card-wrap">
<scroll-view scroll-y="true" class="card-list" @scrolltolower="getMemberCard()">
<block v-if="memberCardData.list.length">
<view class="card-item" :class="{ active: memberCardData.index == index }" v-for="(item, index) in memberCardData.list" :key="index" @click="selectMemberCard(item, index)">
<view class="card-name">{{ item.goods_name }}</view>
<view class="info">
<view v-if="item.total_num > 0">可用{{ item.total_num - item.total_use_num }}</view>
<view v-else>不限次</view>
<view v-if="item.end_time > 0">{{ $util.timeFormat(item.end_time, 'Y/m/d') }}</view>
<view v-else>长期有效</view>
</view>
</view>
</block>
<view v-else class="empty">
<image src="@/static/card/card_empty.png" mode="widthFix"/>
<view class="tips">暂无可用卡项</view>
</view>
</scroll-view>
<view class="item-list">
<view class="title">
<view>可用服务/商品</view>
<view v-if="memberCardData.currData.card_type == 'commoncard'">
<text>以下服务/商品剩余可用</text>
<text class="num">{{ memberCardData.currData.total_num - memberCardData.currData.total_use_num }}</text>
<text></text>
</view>
</view>
<scroll-view scroll-y="true" class="item-wrap">
<view class="uni-flex justify-between content" v-if="memberCardData.currData.item_list">
<view class="card-item" :class="{
active: memberCardData.selected['item_' + item.item_id],
'not-select': !checkStatus(item) && !memberCardData.selected['item_' + item.item_id]
}" @click="selectMemberCardItem(item, index)" v-for="(item, index) in memberCardData.currData.item_list">
<view class="image">
<image v-if="item.sku_image == '@/static/goods/goods.png'" src="@/static/goods/goods.png" mode="widthFix"/>
<image v-else :src="$util.img(item.sku_image.split(',')[0], { size: 'small' })" @error="item.sku_image = '@/static/goods/goods.png'" mode="widthFix"/>
</view>
<view class="info">
<view>
<view class="name">
<text class="tag">{{ item.is_virtual ? '服务' : '商品' }}</text>
<text>{{ item.sku_name }}</text>
</view>
<block v-if="memberCardData.currData.card_type != 'commoncard'">
<view class="num" v-if="item.num > 0">剩余可用{{ item.num - item.use_num }}</view>
<view class="num" v-else>不限次</view>
</block>
</view>
<view class="action-wrap">
<view class="price">
<text class="util"></text>
{{ item.price }}
</view>
<view class="number-wrap" v-if="memberCardData.selected['item_' + item.item_id]">
<text class="iconfont iconjian" @click.stop="itemDec(memberCardData.selected['item_' + item.item_id])"></text>
<input type="number" v-model="memberCardData.selected['item_' + item.item_id].input_num" />
<text class="iconfont iconjia" @click.stop="itemInc(memberCardData.selected['item_' + item.item_id])"></text>
</view>
</view>
</view>
</view>
</view>
<view class="empty" v-else>
<image src="@/static/goods/goods_empty.png" mode="widthFix"/>
<view class="tips">暂无相关数据</view>
</view>
</scroll-view>
<view class="button-wrap">
<button type="default" class="primary-btn" :disabled="memberCardData.itemIndex == -1" @click="selectGoods()">加入购物车</button>
</view>
</view>
</view>
</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import uniDataCheckbox from '@/components/uni-data-checkbox/uni-data-checkbox.vue';
import UniPopup from "../uni-popup/uni-popup";
import index from './index.js';
export default {
name: 'nsMember',
components: {
UniPopup,
uniDataCheckbox
},
mixins: [index]
};
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

View File

@@ -0,0 +1,398 @@
<template>
<view class="member-detail-wrap">
<!-- 卡包 -->
<uni-popup ref="cardListPop">
<view class="pop-box card-list-pop-box">
<view class="pop-header">
<view class="pop-header-text">卡包</view>
<view class="pop-header-close" @click="close('cardlist')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<scroll-view scroll-y="true" class="common-scrollbar pop-content">
<dataTable url="/cardservice/storeapi/membercard/lists" :cols="card" ref="table" :option="option" :pagesize="pageSize">
<template v-slot:action="dataTable">
<text class="view-detail" @click="viewDetails(dataTable.value.card_id)">查看详情</text>
</template>
</dataTable>
</scroll-view>
</view>
</uni-popup>
<uni-popup ref="cardDetailPop">
<view class="pop-box cardDetailPop-box">
<view class="pop-header">
<view class="pop-header-text">详情</view>
<view class="pop-header-close" @click="$refs.cardDetailPop.close()">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<view class="pop-content">
<view class="tab-head">
<text v-for="(item, index) in tabObj.list" :key="index" :class="{ active: tabObj.index == item.value }" v-if="(item.value == 3 && card_detail.card_log && card_detail.card_log.length > 0) || item.value != 3" @click="tabObj.index = item.value">
{{ item.name }}
</text>
</view>
<view class="tab-content">
<view class="basic-info" v-if="tabObj.index == 0">
<view class="basic-item using-hidden">卡项名称{{ basicInfo.goods_name }}</view>
<view class="basic-item">价格{{ basicInfo.price }}</view>
<view class="basic-item">
卡类型{{ (basicInfo.card_type == 'oncecard' && '限次卡') || (basicInfo.card_type == 'timecard' && '限时卡') || (basicInfo.card_type == 'commoncard' && '通用卡') }}
</view>
<view class="basic-item">总次数/已使用{{ basicInfo.card_type == 'timecard' ? '不限' : basicInfo.total_num }}/{{ basicInfo.total_use_num }}</view>
<view class="basic-item">获取时间{{ $util.timeFormat(basicInfo.create_time) }}</view>
<view class="basic-item">到期时间{{ basicInfo.end_time > 0 ? $util.timeFormat(basicInfo.end_time) : '永久有效' }}</view>
</view>
<view class="other-information" v-if="tabObj.index == 1 && basicInfo && basicInfo.card_item">
<view class="information-head">
<text>商品名称</text>
<text>总次数/已使用</text>
<text>有效期</text>
</view>
<view class="information-body">
<view class="information-tr" v-for="(item, index) in basicInfo.card_item" :key="index">
<text class="using-hidden">{{ item.sku_name }}</text>
<text>{{ item.card_type == 'timecard' ? '不限' : item.num }} /{{ item.use_num }}</text>
<text>{{ item.end_time > 0 ? $util.timeFormat(item.end_time) : '永久有效' }}</text>
</view>
<view class="information-tr empty" v-if="!basicInfo.card_item.length">
<view class="iconfont iconwushuju"></view>
<view>暂无数据</view>
</view>
</view>
</view>
<view class="card-info" v-if="tabObj.index == 2">
<dataTable url="/cardservice/storeapi/membercard/records" :cols="cardInfo.card" ref="table" :option="cardInfo.option" :pagesize="cardInfo.pageSize"></dataTable>
</view>
</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import { getMemberCardDetail } from '@/api/member'
import dataTable from '@/components/uni-data-table/uni-data-table.vue';
export default {
components: {
dataTable
},
props: {
option: {}
},
data() {
return {
pageSize: 8,
card: [{
width: 20,
title: '名称',
align: 'left',
field: 'goods_name'
}, {
width: 18,
title: '卡号',
align: 'center',
field: 'card_code'
}, {
width: 8,
title: '卡类型',
align: 'left',
templet: function (data) {
if (data.card_type == 'oncecard') return '限次卡';
if (data.card_type == 'timecard') return '限时卡';
if (data.card_type == 'commoncard') return '通用卡';
}
}, {
width: 12,
title: '总次数/已使用',
align: 'center',
templet: data => {
var totalNum = data.card_type == 'timecard' ? '不限' : data.total_num;
return totalNum + '/' + data.total_use_num;
}
}, {
width: 17,
title: '创建时间',
align: 'center',
templet: data => {
return this.$util.timeFormat(data.create_time);
}
}, {
width: 17,
title: '到期时间',
align: 'center',
templet: data => {
if (data.end_time) return this.$util.timeFormat(data.end_time);
else return '长期有效';
}
}, {
width: 8,
title: '操作',
align: 'right',
action: true
}],
tabObj: {
list: [{
value: 0,
name: '基础信息'
}, {
value: 1,
name: '商品/项目'
}, {
value: 2,
name: '使用记录'
}],
index: 1
},
currCardId: 0,
basicInfo: {},
cardInfo: {
card: [{
width: 40,
title: '卡项名称',
align: 'left',
field: 'sku_name'
}, {
width: 20,
title: '使用次数',
align: 'center',
field: 'num'
}, {
width: 25,
title: '使用时间',
align: 'right',
templet: data => {
return this.$util.timeFormat(data.create_time);
}
}, {
width: 15,
title: '操作',
align: 'right',
action: true
}],
option: {},
pageSize: 6
}
};
},
created() { },
methods: {
open() {
this.$refs.cardListPop.open();
},
close() {
this.$refs.cardListPop.close();
},
viewDetails(card_id) {
this.currCardId = card_id;
this.$refs.cardDetailPop.open();
this.getCardDetail();
this.cardInfo.option.member_id = this.globalMemberInfo.member_id;
this.cardInfo.option.card_id = this.currCardId;
},
getCardDetail() {
let data = {};
data.member_id = this.globalMemberInfo.member_id;
data.card_id = this.currCardId;
getMemberCardDetail(data).then(res => {
this.basicInfo = {};
if (res.code >= 0) {
this.basicInfo = res.data;
}
});
}
}
};
</script>
<style lang="scss" scoped>
.pop-box {
background: #ffffff;
width: 8rem;
height: 7rem;
.pop-header {
padding: 0 0.15rem 0 0.2rem;
height: 0.5rem;
line-height: 0.5rem;
border-bottom: 0.01rem solid #f0f0f0;
font-size: 0.14rem;
color: #333;
overflow: hidden;
border-radius: 0.02rem 0.2rem 0 0;
box-sizing: border-box;
display: flex;
justify-content: space-between;
.pop-header-close {
cursor: pointer;
text {
font-size: 0.18rem;
}
}
}
.pop-content {
height: calc(100% - 1rem);
overflow-y: scroll;
padding: 0.1rem 0.2rem;
box-sizing: border-box;
}
.pop-bottom {
button {
width: 95%;
}
}
}
.card-list-pop-box {
width: 10rem;
height: 5.7rem;
.pop-content {
height: calc(100% - 0.5rem);
}
/deep/ .tpage {
position: absolute;
right: 0;
bottom: 0;
}
.basic-box {
display: flex;
justify-content: space-between;
margin-bottom: 0.2rem;
padding: 0.2rem;
box-sizing: border-box;
}
.basic {
padding: 0.1rem;
margin-bottom: 0.5rem;
}
}
.cardDetailPop-box {
width: 10rem;
height: 5.7rem;
.tab-head {
display: flex;
background-color: #f7f8fa;
text {
height: 0.5rem;
line-height: 0.5rem;
text-align: center;
padding: 0 0.35rem;
box-sizing: border-box;
&.active {
background-color: #fff;
}
}
}
.pop-content {
overflow-y: inherit;
.basic-info {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 0.2rem;
.basic-item {
flex-basis: 33%;
height: 0.4rem;
line-height: 0.4rem;
}
}
}
.other-information {
display: flex;
justify-content: space-between;
flex-direction: column;
padding-top: 0.2rem;
.information-head {
display: flex;
justify-content: space-between;
background-color: #f7f8fa;
text {
padding: 0 0.2rem;
height: 0.5rem;
line-height: 0.5rem;
&:nth-child(1) {
flex-basis: 35%;
}
&:nth-child(2) {
flex-basis: 35%;
}
&:nth-child(2) {
flex-basis: 30%;
}
}
}
.information-tr {
display: flex;
justify-content: space-between;
border-bottom: 0.01rem solid #e6e6e6;
text {
padding: 0 0.2rem;
height: 0.5rem;
line-height: 0.5rem;
&:nth-child(1) {
flex-basis: 35%;
}
&:nth-child(2) {
flex-basis: 35%;
}
&:nth-child(2) {
flex-basis: 30%;
}
}
&.empty {
display: flex;
justify-content: center;
align-items: center;
height: 0.5rem;
color: #909399;
.iconfont {
font-size: 0.25rem;
margin: 0.05rem;
}
}
}
}
.card-info {
display: flex;
justify-content: space-between;
padding-top: 0.2rem;
}
}
.view-detail {
color: $primary-color;
}</style>

View File

@@ -0,0 +1,183 @@
import {
getMemberInfoById,
getMemberLevelList,
getCouponTypeList,
sendMemberCoupon,
applyingMembershipCard
} from '@/api/member'
export default {
data() {
return {
pageSize: 8,
sex: [{
text: '未知',
value: 0
}, {
text: '男',
value: 1
}, {
text: '女',
value: 2
}],
sendCoupon: {
list: [],
page: 1
},
memberLevelList: [],
applyMember: {
level_id: '',
member_level: '',
member_level_name: '',
member_code: ''
}
};
},
created() {
this.getMemberLevel();
},
methods: {
open() {
this.getMemberInfo(); // 保证数据实时性
this.$refs.memberPop.open();
},
getMemberInfo() {
getMemberInfoById(this.globalMemberInfo.member_id).then(res => {
if (res.code >= 0) {
res.data.birthday = res.data.birthday > 0 ? this.$util.timeFormat(res.data.birthday, 'Y-m-d') : '--';
this.$store.commit('app/setGlobalMemberInfo', res.data);
}
});
},
getMemberLevel() {
this.memberLevelList = [];
getMemberLevelList().then(res => {
if (res.code == 0 && res.data) {
for (let i in res.data) {
this.memberLevelList.push({
label: res.data[i]['level_name'],
value: res.data[i]['level_id'].toString(),
disabled: false
});
}
}
});
},
selectMemberLevel(index, item) {
if (index >= 0) {
this.applyMember.level_id = item.value;
this.applyMember.member_level = item.value;
this.applyMember.member_level_name = item.label;
} else {
this.applyMember.level_id = '';
this.applyMember.member_level = item.value;
this.applyMember.member_level_name = item.label;
}
},
// 客户操作
memberAction(type) {
switch (type) {
case 'sendCoupon':
this.getCouponList();
this.$refs.sendCouponPop.open('center');
break;
case 'applyMember':
this.$refs.applyMemberPop.open();
break;
}
},
popClose(type) {
this.$refs[type + 'Pop'].close();
},
//获取发放优惠券列表
getCouponList() {
let data = {
page: this.sendCoupon.page,
page_size: 7
};
getCouponTypeList(data).then(res => {
if (res.code >= 0) {
if (this.sendCoupon.page == 1) this.sendCoupon.list = [];
if (res.data.list && res.data.list.length) {
res.data.list.forEach((item, index) => {
if (item.validity_type == 0) item.validity_name = '失效日期:' + this.$util.timeFormat(item.end_time);
else if (item.validity_type == 1) item.validity_name = '领取后,' + item.fixed_term + '天有效';
else item.validity_name = '长期有效';
item.num = 0;
});
}
this.sendCoupon.list = this.sendCoupon.list.concat(res.data.list);
if (res.data.page_count >= this.sendCoupon.page) this.sendCoupon.page++;
}
});
},
// 发放数量
dec: function (item) {
if (item.num > 0) {
item.num = item.num - 1;
}
},
inc: function (item) {
item.num = item.num + 1;
},
// 发放优惠券
sendCouponFn() {
if (!this.sendCoupon.list || !this.sendCoupon.list.length) return false;
let data = {};
data.member_id = this.globalMemberInfo.member_id;
data.coupon_data = '';
let couponDataArr = [];
this.sendCoupon.list.forEach((item, index) => {
if (item.num > 0) {
let obj = {};
obj.coupon_type_id = item.coupon_type_id;
obj.num = item.num;
couponDataArr.push(obj);
}
});
if (couponDataArr.length <= 0) return false;
data.coupon_data = JSON.stringify(couponDataArr);
sendMemberCoupon(data).then(res => {
this.$util.showToast({
title: res.message
});
if (res.code >= 0) {
this.sendCoupon.page = 1;
this.sendCoupon.list = [];
this.getMemberInfo();
this.$refs.sendCouponPop.close();
}
});
},
//打开会员卡项
showMemberCard() {
this.$refs.memberCardPopup.open();
},
// 办理会员卡
saveApplyMember() {
if (!this.applyMember.level_id) {
this.$util.showToast({
title: '请选择会员卡等级'
});
return false;
}
applyingMembershipCard({
member_id: this.globalMemberInfo.member_id,
level_id: this.applyMember.level_id,
member_code: this.applyMember.member_code
}).then(res => {
this.$util.showToast({
title: res.message
});
if (res.code >= 0) {
this.getMemberInfo();
this.popClose('applyMember');
}
});
},
headError(item) {
item.headimg = this.defaultImg.head;
}
}
}

View File

@@ -0,0 +1,412 @@
.member-detail-wrap {
width: 100%;
border-left: 0;
.member-head {
height: 0.66rem;
line-height: 0.66rem;
box-sizing: border-box;
border-bottom: 0.01rem solid #e6e6e6;
font-size: 0.14rem;
}
.member-content {
padding: 0.15rem;
width: 100%;
height: calc(100vh - 0.8rem);
box-sizing: border-box;
.content-block {
width: 100%;
box-sizing: border-box;
display: flex;
align-items: center;
.item-img {
width: 0.7rem;
height: 0.7rem;
border-radius: 50%;
box-sizing: border-box;
image {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.item-content {
padding-left: 0.15rem;
width: calc(100% - 0.7rem);
box-sizing: border-box;
.item-title {
width: 100%;
font-size: 0.16rem;
align-items: center;
display: flex;
.item-label {
border: 0.01rem solid $primary-color;
color: $primary-color;
background-color: #fff;
border-radius: 0.02rem;
width: fit-content;
padding: 0.01rem 0.05rem;
margin-left: 0.15rem;
}
.item-title-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 50%;
font-size: 0.16rem;
}
}
.info-list {
margin-top: 0.1rem;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.info-item {
font-size: .14rem;
padding-right: .2rem;
width: 50%;
box-sizing: border-box;
height: .25rem;
line-height: .25rem;
}
}
}
}
.content-block.account {
border: 0.01rem solid #e6e6e6;
background-color: #ffffff;
display: flex;
flex-wrap: wrap;
margin-top: 0.2rem;
border-radius: 0.03rem;
align-items: baseline;
padding: .1rem 0;
.content-data-item {
padding: .1rem 0;
width: 33%;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
.data-item-title {
}
.data-item-value {
font-size: 0.26rem;
margin-top: 0.1rem;
}
}
}
.content-block.assets {
display: flex;
justify-content: space-around;
margin-top: 0.2rem;
.content-data-left {
background-color: #ffffff;
padding: 0.25rem 0;
border-radius: 0.03rem;
width: calc(50% - 0.075rem);
margin-right: 0.15rem;
display: flex;
justify-content: space-around;
height: 1rem;
.content-data-item {
.data-item-title {
}
.data-item-value {
font-size: 0.26rem;
margin-top: 0.1rem;
}
}
}
}
.content-block.action {
display: flex;
justify-content: flex-start;
margin-top: 0.2rem;
.content-data-item {
border: 0.01rem solid #e6e6e6;
width: calc(100% / 3);
background-color: #ffffff;
display: flex;
padding: 0.15rem 0;
border-radius: 0.03rem;
align-items: center;
text-align: center;
flex-direction: column;
margin-right: 0.15rem;
cursor: pointer;
.data-item-icon {
width: 0.55rem;
height: 0.55rem;
image {
width: 100%;
height: 100%;
}
}
.data-item-value {
margin-top: 0.1rem;
}
&:last-child {
margin-right: 0;
}
}
}
}
}
// pop弹框
.pop-box {
background: #ffffff;
width: 8rem;
height: 7rem;
.pop-header {
padding: 0 0.15rem 0 0.2rem;
height: 0.5rem;
line-height: 0.5rem;
border-bottom: 0.01rem solid #f0f0f0;
font-size: 0.14rem;
color: #333;
overflow: hidden;
border-radius: 0.02rem 0.2rem 0 0;
box-sizing: border-box;
display: flex;
justify-content: space-between;
.pop-header-text {
}
.pop-header-close {
cursor: pointer;
text {
font-size: 0.18rem;
}
}
}
.pop-content {
height: calc(100% - 1rem);
overflow-y: scroll;
padding: 0.1rem 0.2rem;
box-sizing: border-box;
}
.pop-bottom {
button {
width: 95%;
}
}
}
//表单
.form-content {
display: flex;
flex-direction: column;
align-items: center;
.form-item {
margin-bottom: 0.1rem;
display: flex;
&:last-of-type {
margin-bottom: 0;
}
.form-label {
width: 1.2rem;
text-align: right;
padding-right: 0.1rem;
box-sizing: border-box;
height: 0.32rem;
line-height: 0.32rem;
.required {
color: red;
margin-right: 0.03rem;
}
}
.form-inline {
width: 2.4rem;
line-height: 0.32rem;
margin-right: 0.1rem;
box-sizing: border-box;
.form-input {
border-width: 0.01rem;
border-style: solid;
background-color: #fff;
color: rgba(0, 0, 0, 0.85);
border-radius: 0.02rem;
padding-left: 0.1rem;
height: 0.32rem;
line-height: 0.32rem;
font-size: 0.14rem;
border-color: #e6e6e6;
}
.word-aux {
color: #999;
font-size: 0.12rem;
line-height: 1.5;
margin-top: 0.05rem;
}
}
}
}
.member-info-wrap {
width: 5.5rem;
height: 5.2rem;
}
.applyMemberPop-box {
width: 6rem;
height: 3.38rem;
.pop-content {
overflow: initial;
}
}
.sendCoupon-box {
width: 9rem;
height: 5.06rem;
.sendCoupon-content {
padding: 0.1rem 0.2rem;
.coupon-table-head {
display: flex;
background: #f7f8fa;
}
.coupon-table-body {
height: 3.2rem;
.coupon-table-tr {
display: flex;
border-bottom: 0.01rem solid #e6e6e6;
}
.table-input {
height: 0.3rem;
line-height: 0.3rem;
border: 0.01rem solid #e6e6e6;
padding: 0 0.1rem;
text-align: center;
}
.item-num {
display: flex;
align-items: center;
margin-left: 0.1rem;
.num-dec {
width: 0.6rem;
height: 0.25rem;
background: #e6e6e6;
border: 0.01rem solid #e6e6e6;
border-radius: 30%;
text-align: center;
line-height: 0.23rem;
font-size: 0.25rem;
margin-right: 0.1rem;
cursor: pointer;
transition: 0.3s;
}
.num-inc {
width: 0.6rem;
height: 0.25rem;
background: $primary-color;
border: 0.01rem solid #e6e6e6;
border-radius: 30%;
text-align: center;
line-height: 0.23rem;
font-size: 0.25rem;
margin-left: 0.1rem;
cursor: pointer;
transition: 0.3s;
color: #fff;
}
}
.coupon-table-td:nth-child(4) {
padding: 0 0.05rem;
}
}
.coupon-table-td,
.coupon-table-th {
padding: 0 0.1rem;
display: flex;
align-items: center;
height: 0.5rem;
&:nth-child(1) {
flex-basis: 30%;
}
&:nth-child(2) {
flex-basis: 20%;
}
&:nth-child(3) {
flex-basis: 30%;
}
&:nth-child(4) {
justify-content: flex-end;
flex-basis: 20%;
text-align: right;
}
}
}
.pop-bottom {
margin-top: 0.12rem;
}
.empty {
display: flex;
align-items: center;
justify-content: center;
height: 0.5rem;
border-bottom: 0.01rem solid #e6e6e6;
color: #909399;
.iconfont {
font-size: 0.25rem;
margin: 0.05rem;
}
}
}

View File

@@ -0,0 +1,202 @@
<template>
<view class="member-detail-wrap">
<uni-popup ref="memberPop">
<view class="pop-box member-info-wrap" v-if="globalMemberInfo">
<view class="pop-header">
<view class="pop-header-text">会员详情</view>
<view class="pop-header-close" @click="popClose('member')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<view class="member-content">
<view class="content-block">
<view class="item-img">
<image mode="aspectFill" v-if="globalMemberInfo.headimg" :src="$util.img(globalMemberInfo.headimg)" @error="headError(globalMemberInfo)"/>
<image mode="aspectFill" v-else :src="$util.img(defaultImg.head)"/>
</view>
<view class="item-content">
<view class="item-title">
<view class="item-title-text">{{ globalMemberInfo.nickname ? globalMemberInfo.nickname : '' }}</view>
<view class="item-label" v-if="globalMemberInfo.member_level && globalMemberInfo.member_level_name">{{ globalMemberInfo.member_level_name }}</view>
</view>
<view class="info-list">
<view class="info-item">手机{{ globalMemberInfo.mobile ? globalMemberInfo.mobile : '' }}</view>
<view class="info-item" v-if="globalMemberInfo.sex == 0">性别未知</view>
<view class="info-item" v-if="globalMemberInfo.sex == 1">性别</view>
<view class="info-item" v-if="globalMemberInfo.sex == 2">性别</view>
<view class="info-item">生日{{ globalMemberInfo.birthday }}</view>
<view class="info-item" v-if="globalMemberInfo.member_time">成为会员{{ $util.timeFormat(globalMemberInfo.member_time,'Y-m-d') }}</view>
</view>
</view>
</view>
<view class="content-block account">
<view class="content-data-item">
<view class="data-item-title">积分</view>
<view class="data-item-value">{{ globalMemberInfo.point ? parseInt(globalMemberInfo.point) : '0' }}</view>
</view>
<view class="content-data-item">
<view class="data-item-title">储值余额()</view>
<view class="data-item-value">{{ globalMemberInfo.balance ? globalMemberInfo.balance : '0.00' }}</view>
</view>
<view class="content-data-item">
<view class="data-item-title">现金余额()</view>
<view class="data-item-value">{{ globalMemberInfo.balance_money ? globalMemberInfo.balance_money : '0.00' }}</view>
</view>
<view class="content-data-item">
<view class="data-item-title">成长值</view>
<view class="data-item-value">{{ globalMemberInfo.growth ? globalMemberInfo.growth : '0' }}</view>
</view>
<view class="content-data-item">
<view class="data-item-title">优惠券()</view>
<view class="data-item-value">{{ globalMemberInfo.coupon_num ? globalMemberInfo.coupon_num : '0' }}</view>
</view>
<view class="content-data-item">
<view class="data-item-title">卡包</view>
<view class="data-item-value">{{ globalMemberInfo.card_num ? globalMemberInfo.card_num : '0' }}
</view>
</view>
</view>
<view class="content-block action">
<view class="content-data-item" @click="memberAction('sendCoupon')">
<view class="data-item-icon">
<image mode="aspectFit" src="@/static/member/icon-member-coupon.png" />
</view>
<view class="data-item-value">送优惠券</view>
</view>
<view class="content-data-item" v-if="isShowMemberCard" @click="showMemberCard">
<view class="data-item-icon">
<image mode="aspectFit" src="@/static/member/icon-member-balance.png" />
</view>
<view class="data-item-value">会员卡项</view>
</view>
<view class="content-data-item" @click="memberAction('applyMember')" v-if="!globalMemberInfo.is_member">
<view class="data-item-icon">
<image mode="aspectFit" src="@/static/member/icon-member-apply.png" />
</view>
<view class="data-item-value">办理会员</view>
</view>
</view>
</view>
</view>
</uni-popup>
<!-- 发放优惠券 -->
<uni-popup ref="sendCouponPop">
<view class="pop-box sendCoupon-box">
<view class="pop-header">
<view class="pop-header-text">送优惠券</view>
<view class="pop-header-close" @click="popClose('sendCoupon')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<view class="common-scrollbar sendCoupon-content">
<view class="coupon-table-head">
<view class="coupon-table-th">优惠券名称</view>
<view class="coupon-table-th">金额</view>
<view class="coupon-table-th">有效期</view>
<view class="coupon-table-th">发放数量</view>
</view>
<scroll-view class="coupon-table-body" @scrolltolower="getCouponList()" scroll-y="true">
<view class="coupon-table-tr" v-for="(item, index) in sendCoupon.list" :key="index">
<view class="coupon-table-td">{{ item.coupon_name }}</view>
<view class="coupon-table-td">{{ item.money }}</view>
<view class="coupon-table-td">{{ item.validity_name }}</view>
<view class="coupon-table-td">
<view class="item-num">
<view class="num-dec" v-on:click="dec(item)">-</view>
<input class="table-input" type="text" v-model="item.num" />
<view class="num-inc" v-on:click="inc(item)">+</view>
</view>
</view>
</view>
<view class="empty" v-if="!sendCoupon.list.length">
<view class="iconfont iconwushuju"></view>
<view>暂无数据</view>
</view>
</scroll-view>
</view>
<view class="pop-bottom">
<button v-if="sendCoupon.list.length" class="primary-btn" @click="sendCouponFn">发放优惠券</button>
</view>
</view>
</uni-popup>
<!-- 办理会员 -->
<uni-popup ref="applyMemberPop">
<view class="pop-box applyMemberPop-box">
<view class="pop-header">
<view class="pop-header-text">办理会员</view>
<view class="pop-header-close" @click="popClose('applyMember')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<view class="common-scrollbar pop-content">
<view class="form-content">
<view class="form-item">
<view class="form-label">
<text class="required"></text>
会员等级
</view>
<view class="form-inline">
<select-lay :zindex="10" :value="applyMember.level_id" name="names" placeholder="请选择会员等级" :options="memberLevelList" @selectitem="selectMemberLevel"/>
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
会员卡号
</view>
<view class="form-inline">
<input class="form-input" type="text" placeholder="请输入会员卡号" v-model="applyMember.member_code" />
<view class="word-aux">会员卡号为会员唯一编号若不设置将会自动生成</view>
</view>
</view>
</view>
</view>
<view class="pop-bottom">
<button class="primary-btn" @click="saveApplyMember">确定</button>
</view>
</view>
</uni-popup>
<!-- 会员卡项弹出框 -->
<ns-member-card-popup v-if="isShowMemberCard" ref="memberCardPopup"/>
</view>
</template>
<script>
import dataTable from '@/components/uni-data-table/uni-data-table.vue';
import index from './index.js';
import UniPopup from "../uni-popup/uni-popup";
export default {
components: {
UniPopup,
dataTable
},
props:{
// 是否展示会员卡项
isShowMemberCard:{
type:Boolean,
default:false
}
},
mixins: [index],
};
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>
<style>
.member-info-pop >>> .pop-content, .member-info-pop >>> .uni-scroll-view{
overflow: inherit !important;
}
</style>

View File

@@ -0,0 +1,502 @@
import {
getMemberInfoById,
getMemberLevelList,
getCouponTypeList,
sendMemberCoupon,
editMember,
modifyMemberPoint,
modifyMemberBalance,
modifyMemberGrowth,
applyingMembershipCard
} from '@/api/member'
export default {
props: {
memberId: {
type: [String, Number],
default: 0
}
},
data() {
return {
pageSize: 8,
endTime: '',
memberInfo: null,
sex: [{
text: '未知',
value: 0
}, {
text: '男',
value: 1
}, {
text: '女',
value: 2
}],
pointData: {
num: 0,
desc: ''
},
growthData: {
num: 0,
desc: ''
},
balanceData: {
num: 0,
desc: ''
},
option: {},
sendCoupon: {
list: [],
page: 1
},
memberLevelList: [],
applyMember: {
level_id: '',
member_level_name: '',
member_code: ''
},
couponCols: [{
width: 15,
title: '优惠券名称',
align: 'left',
field: 'coupon_name'
}, {
width: 7,
title: '类型',
align: 'left',
templet: function (data) {
if (data.type == 'reward') return '满减';
if (data.type == 'discount') return '折扣';
}
}, {
width: 18,
title: '优惠金额',
align: 'left',
templet: function (data) {
if (data.type == 'reward') {
var html = `${data.at_least}元减${data.money}`;
return `<view title="${html}">${html}</view>`;
}
if (data.type == 'discount') {
var text = '满' + data.at_least + '元打' + data.discount + '折';
if (data.discount_limit) text += '(最多抵扣' + data.discount_limit + '元)';
return '<view title="' + text + '">' + text + '</view>';
}
}
}, {
width: 17,
title: '有效期',
align: 'center',
templet: data => {
if (data.end_time) return this.$util.timeFormat(data.end_time);
else return '长期有效';
}
}, {
width: 10,
title: '状态',
align: 'center',
return: data => {
if (data.state == 1) return '未使用';
if (data.state == 2) return '已使用';
if (data.state == 3) return '已过期';
}
},{
title: '适用场景',
field: 'use_channel_name',
width: 15,
align: 'left',
}, {
width: 18,
title: '领取时间',
align: 'right',
templet: data => {
return this.$util.timeFormat(data.fetch_time);
}
}],
pointCols: [{
width: 20,
title: '积分',
align: 'left',
field: 'account_data'
}, {
width: 25,
title: '发生方式',
align: 'left',
field: 'type_name'
}, {
width: 25,
title: '发生时间',
align: 'left',
templet: data => {
var html = this.$util.timeFormat(data.create_time);
return html;
}
}, {
width: 30,
title: '备注',
align: 'left',
field: 'remark'
}],
balanceCols: [{
width: 10,
title: '账户类型',
align: 'left',
field: 'account_type_name'
}, {
width: 15,
title: '余额',
align: 'left',
field: 'account_data'
}, {
width: 20,
title: '发生方式',
align: 'left',
field: 'type_name'
}, {
width: 25,
title: '发生时间',
align: 'left',
templet: data => {
var html = this.$util.timeFormat(data.create_time);
return html;
}
}, {
width: 30,
title: '备注',
align: 'left',
field: 'remark'
}],
growthCols: [{
width: 20,
title: '成长值',
align: 'left',
field: 'account_data'
}, {
width: 25,
title: '发生方式',
align: 'left',
field: 'type_name'
}, {
width: 25,
title: '发生时间',
align: 'left',
templet: data => {
var html = this.$util.timeFormat(data.create_time);
return html;
}
}, {
width: 30,
title: '备注',
align: 'left',
field: 'remark'
}],
};
},
created() {
this.getMemberInfo();
this.getMemberLevel();
let date = new Date();
var y = date.getFullYear();
var m = date.getMonth() + 1;
var d = date.getDate();
this.endTime = y + '-' + m + '-' + d;
},
watch: {
memberId: function () {
this.getMemberInfo();
}
},
methods: {
checkAdmin() {
if (this.userInfo && this.userInfo.is_admin == 0) {
// 检查当前账号是否有修改手机号的权限
var isAgree = false;
this.userInfo.user_group_list.forEach((item) => {
if (item.store_id == this.globalStoreInfo.store_id) {
if (item.menu_array.indexOf('member_edit') != -1) {
isAgree = true;
}
}
});
if (isAgree) {
return false;
}
}
return true;
},
getMemberInfo() {
getMemberInfoById(this.memberId).then(res => {
if (res.code >= 0) {
res.data.birthday = res.data.birthday > 0 ? this.$util.timeFormat(res.data.birthday, 'Y-m-d') : '';
this.memberInfo = res.data;
}
});
},
getMemberLevel() {
this.memberLevelList = [];
getMemberLevelList().then(res => {
if (res.code == 0 && res.data) {
for (let i in res.data) {
this.memberLevelList.push({
label: res.data[i]['level_name'],
value: res.data[i]['level_id'].toString(),
disabled: false
});
}
}
});
},
selectMemberLevel(index, item) {
if (index >= 0) {
this.applyMember.level_id = item.value;
this.applyMember.member_level_name = item.label;
this.memberInfo.member_level = item.value;
} else {
this.applyMember.level_id = '';
this.applyMember.member_level_name = '';
this.memberInfo.member_level = '';
}
},
// 客户操作
memberAction(type) {
switch (type) {
case 'memberInfo':
this.$refs.memberInfoPop.open('center');
break;
case 'point':
this.$refs.pointPop.open('center');
break;
case 'balance':
this.$store.commit('app/setGlobalMemberInfo', this.memberInfo);
this.$util.redirectTo('/pages/recharge/index');
break;
case 'sendCoupon':
this.getCouponList();
this.$refs.sendCouponPop.open('center');
break;
case 'growth':
this.$refs.growthPop.open('center');
break;
case 'couponList':
this.option = {
member_id: this.memberId
};
this.$refs.couponListPop.open('center');
break;
case 'cardList':
this.option = {
member_id: this.memberId,
status:1,
};
this.$refs.memberCardRecord.open('center');
break;
case 'pointList':
// 积分列表
this.option = {
member_id: this.memberId,
account_type: 'point'
};
this.$refs.pointListPop.open();
break;
case 'balanceList':
// 余额列表
this.option = {
member_id: this.memberId,
account_type: 'balance'
};
this.$refs.balanceListPop.open();
break;
case 'growthList':
// 成长值列表
this.option = {
member_id: this.memberId,
account_type: 'growth'
};
this.$refs.growthListPop.open();
break;
case 'applyMember':
this.$refs.applyMemberPop.open();
break;
}
},
popClose(type) {
this.$refs[type + 'Pop'].close();
},
//获取发放优惠券列表
getCouponList() {
let data = {
page: this.sendCoupon.page,
page_size: 7
};
getCouponTypeList(data).then(res => {
if (res.code >= 0) {
if (this.sendCoupon.page == 1) this.sendCoupon.list = [];
if (res.data.list && res.data.list.length) {
res.data.list.forEach((item, index) => {
if (item.validity_type == 0) item.validity_name = '失效日期:' + this.$util.timeFormat(item.end_time);
else if (item.validity_type == 1) item.validity_name = '领取后,' + item.fixed_term + '天有效';
else item.validity_name = '长期有效';
item.num = 0;
});
}
this.sendCoupon.list = this.sendCoupon.list.concat(res.data.list);
if (res.data.page_count >= this.sendCoupon.page) this.sendCoupon.page++;
}
});
},
// 发放数量
dec: function (item) {
if (item.num > 0) {
item.num = item.num - 1;
}
},
inc: function (item) {
item.num = item.num + 1;
},
// 发放优惠券
sendCouponFn() {
if (!this.sendCoupon.list || !this.sendCoupon.list.length) return false;
let data = {};
data.member_id = this.memberInfo.member_id;
data.coupon_data = '';
let couponDataArr = [];
this.sendCoupon.list.forEach((item, index) => {
if (item.num > 0) {
let obj = {};
obj.coupon_type_id = item.coupon_type_id;
obj.num = item.num;
couponDataArr.push(obj);
}
});
if (couponDataArr.length <= 0) return false;
data.coupon_data = JSON.stringify(couponDataArr);
sendMemberCoupon(data).then(res => {
this.$util.showToast({
title: res.message
});
if (res.code >= 0) {
this.sendCoupon.page = 1;
this.sendCoupon.list = [];
this.getMemberInfo();
this.$refs.sendCouponPop.close();
}
});
},
//修改客户信息
saveMemberInfo() {
let data = {
nickname: this.memberInfo.nickname,
sex: this.memberInfo.sex,
birthday: this.memberInfo.birthday,
member_id: this.memberInfo.member_id,
level_id: this.memberInfo.member_level
};
if (this.checkAdmin()) {
data.mobile = this.memberInfo.mobile;
}
editMember(data).then(res => {
this.$util.showToast({
title: res.message
});
if (res.code >= 0) {
this.getMemberInfo();
this.popClose('memberInfo');
}
});
},
// 调整积分
savePoint() {
if (parseInt(this.pointData.num) < 0 && parseInt(this.memberInfo.point) < parseInt(this.pointData.num * -1)) {
this.$util.showToast({
title: '调整数额与当前积分之和不能小于0'
});
return false;
}
modifyMemberPoint({
member_id: this.memberInfo.member_id,
adjust_num: this.pointData.num,
remark: this.pointData.desc
}).then(res => {
this.$util.showToast({
title: res.message
});
if (res.code >= 0) {
this.pointData.num = 0;
this.pointData.desc = '';
this.getMemberInfo();
this.popClose('point');
}
});
},
// 调整余额
saveBalance() {
modifyMemberBalance({
member_id: this.memberInfo.member_id,
adjust_num: this.balanceData.num,
remark: this.balanceData.desc
}).then(res => {
this.$util.showToast({
title: res.message
});
if (res.code >= 0) {
this.balanceData.num = 0;
this.balanceData.desc = '';
this.getMemberInfo();
this.popClose('balance');
}
});
},
// 调整成长值
saveGrowth() {
if (parseInt(this.growthData.num) < 0 && parseInt(this.memberInfo.growth) < parseInt(this.growthData.num * -1)) {
this.$util.showToast({
title: '调整数额与当前成长值之和不能小于0'
});
return false;
}
modifyMemberGrowth({
member_id: this.memberInfo.member_id,
adjust_num: this.growthData.num,
remark: this.growthData.desc
}).then(res => {
this.$util.showToast({
title: res.message
});
if (res.code >= 0) {
this.growthData.num = 0;
this.growthData.desc = '';
this.getMemberInfo();
this.popClose('growth');
}
});
},
// 办理会员卡
saveApplyMember() {
if (!this.applyMember.level_id) {
this.$util.showToast({
title: '请选择会员卡等级'
});
return false;
}
applyingMembershipCard({
member_id: this.memberInfo.member_id,
level_id: this.applyMember.level_id,
member_code: this.applyMember.member_code
}).then(res => {
this.$util.showToast({
title: res.message
});
if (res.code >= 0) {
this.$root.page = 1;
this.$root.search_text = 1;
this.$root.getMemberListFn();
this.popClose('applyMember');
}
});
},
headError(item) {
item.headimg = this.defaultImg.head;
}
}
}

View File

@@ -0,0 +1,453 @@
.member-detail-wrap {
width: 100%;
border-left: 0;
.member-head {
height: 0.66rem;
line-height: 0.66rem;
box-sizing: border-box;
border-bottom: 0.01rem solid #e6e6e6;
font-size: 0.14rem;
}
.member-content {
padding: 0.15rem;
width: 100%;
height: calc(100vh - 0.8rem);
box-sizing: border-box;
.content-block {
width: 100%;
box-sizing: border-box;
display: flex;
align-items: center;
.item-img {
width: 0.7rem;
height: 0.7rem;
border-radius: 50%;
box-sizing: border-box;
image {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.item-content {
padding-left: 0.15rem;
width: calc(100% - 0.7rem);
box-sizing: border-box;
.item-title {
width: 100%;
font-size: 0.16rem;
align-items: center;
display: flex;
.item-label {
border: 0.01rem solid $primary-color;
color: $primary-color;
background-color: #fff;
border-radius: 0.02rem;
width: fit-content;
padding: 0.01rem 0.05rem;
margin-left: 0.15rem;
}
.item-title-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 50%;
font-size: 0.16rem;
}
}
.info-list {
margin-top: 0.15rem;
display: flex;
justify-content: space-between;
.info-item {
font-size: 0.14rem;
margin-right: 0.2rem;
}
}
}
}
.content-block.account {
border: 0.01rem solid #e6e6e6;
background-color: #ffffff;
padding: 0.25rem 0;
display: flex;
justify-content: space-around;
margin-top: 0.2rem;
border-radius: 0.03rem;
align-items: baseline;
.content-data-item {
.data-item-title {}
.data-item-value {
font-size: 0.26rem;
margin-top: 0.1rem;
}
.data-item-action {
margin-top: 0.1rem;
color: $primary-color;
cursor: pointer;
float: left;
&:nth-child(2n) {
margin-left: 0.1rem;
}
}
}
}
.content-block.assets {
display: flex;
justify-content: space-around;
margin-top: 0.2rem;
.content-data-left {
background-color: #ffffff;
padding: 0.25rem 0;
border-radius: 0.03rem;
width: calc(50% - 0.075rem);
margin-right: 0.15rem;
display: flex;
justify-content: space-around;
height: 1rem;
.content-data-item {
.data-item-title {}
.data-item-value {
font-size: 0.26rem;
margin-top: 0.1rem;
}
.data-item-action {
margin-top: 0.15rem;
color: $primary-color;
cursor: pointer;
}
}
}
}
.content-block.action {
display: flex;
justify-content: flex-start;
margin-top: 0.2rem;
.content-data-item {
border: 0.01rem solid #e6e6e6;
width: calc(100% / 6);
background-color: #ffffff;
display: flex;
padding: 0.15rem 0;
border-radius: 0.03rem;
align-items: center;
text-align: center;
flex-direction: column;
margin-right: 0.15rem;
cursor: pointer;
.data-item-icon {
width: 0.55rem;
height: 0.55rem;
image {
width: 100%;
height: 100%;
}
}
.data-item-value {
margin-top: 0.1rem;
}
&:last-child {
margin-right: 0;
}
}
}
}
}
// pop弹框
.pop-box {
background: #ffffff;
width: 8rem;
height: 7rem;
.pop-header {
padding: 0 0.15rem 0 0.2rem;
height: 0.5rem;
line-height: 0.5rem;
border-bottom: 0.01rem solid #f0f0f0;
font-size: 0.14rem;
color: #333;
overflow: hidden;
border-radius: 0.02rem 0.2rem 0 0;
box-sizing: border-box;
display: flex;
justify-content: space-between;
.pop-header-close {
cursor: pointer;
text {
font-size: 0.18rem;
}
}
}
.pop-content {
height: calc(100% - 1.05rem);
overflow-y: auto;
padding: 0.1rem 0.2rem;
box-sizing: border-box;
}
.pop-bottom {
width: 100%;
box-sizing: border-box;
padding: 0.1rem 0.2rem;
button {
width: 100%;
line-height: 0.35rem;
height: 0.35rem;
}
}
}
//表单
.form-content {
display: flex;
flex-direction: column;
align-items: center;
.form-item {
margin-bottom: 0.1rem;
display: flex;
&:last-of-type {
margin-bottom: 0;
}
.form-label {
width: 1.2rem;
text-align: right;
padding-right: 0.1rem;
box-sizing: border-box;
height: 0.32rem;
line-height: 0.32rem;
.required {
color: red;
margin-right: 0.03rem;
}
}
.form-inline {
width: 2.5rem;
line-height: 0.32rem;
box-sizing: border-box;
.form-input {
border-width: 0.01rem;
border-style: solid;
background-color: #fff;
color: rgba(0, 0, 0, 0.85);
border-radius: 0.02rem;
padding-left: 0.1rem;
height: 0.32rem;
line-height: 0.32rem;
font-size: 0.14rem;
border-color: #e6e6e6;
}
.form-textarea {
border-width: 0.01rem;
border-style: solid;
background-color: #fff;
color: rgba(0, 0, 0, 0.85);
border-radius: 0.02rem;
padding-left: 0.1rem;
line-height: 0.32rem;
font-size: 0.14rem;
border-color: #e6e6e6;
width: 95%;
}
.word-aux {
color: #999;
font-size: 0.12rem;
line-height: 1.5;
margin-top: 0.05rem;
}
}
}
}
.memberInfo-box {
width: 5.2rem;
height: 4.31rem;
}
.pointPop-box {
width: 4.2rem;
height: 3.94rem;
}
.balancePop-box {
width: 4.2rem;
height: 4.2rem;
}
.coupon-list-pop-box {
width: 10rem;
height: 5.7rem;
.pop-content {
height: calc(100% - 0.5rem);
}
/deep/ .tpage {
position: absolute;
right: 0;
bottom: 0;
}
}
.applyMemberPop-box {
width: 4.2rem;
height: 3.38rem;
.pop-content {
overflow: initial;
}
}
.sendCoupon-box {
width: 9rem;
height: 5.06rem;
.sendCoupon-content {
padding: 0.1rem 0.2rem;
.coupon-table-head {
display: flex;
background: #f7f8fa;
}
.coupon-table-body {
height: 3.2rem;
.coupon-table-tr {
display: flex;
border-bottom: 0.01rem solid #e6e6e6;
}
.table-input {
height: 0.3rem;
line-height: 0.3rem;
border: 0.01rem solid #e6e6e6;
padding: 0 0.1rem;
text-align: center;
}
.item-num {
display: flex;
align-items: center;
margin-left: 0.1rem;
.num-dec {
width: 0.6rem;
height: 0.25rem;
background: #e6e6e6;
border: 0.01rem solid #e6e6e6;
border-radius: 30%;
text-align: center;
line-height: 0.23rem;
font-size: 0.25rem;
margin-right: 0.1rem;
cursor: pointer;
transition: 0.3s;
}
.num-inc {
width: 0.6rem;
height: 0.25rem;
background: $primary-color;
border: 0.01rem solid #e6e6e6;
border-radius: 30%;
text-align: center;
line-height: 0.23rem;
font-size: 0.25rem;
margin-left: 0.1rem;
cursor: pointer;
transition: 0.3s;
color: #fff;
}
}
.coupon-table-td:nth-child(4) {
padding: 0 0.05rem;
}
}
.coupon-table-td,
.coupon-table-th {
padding: 0 0.1rem;
display: flex;
align-items: center;
height: 0.5rem;
&:nth-child(1) {
flex-basis: 30%;
}
&:nth-child(2) {
flex-basis: 20%;
}
&:nth-child(3) {
flex-basis: 30%;
}
&:nth-child(4) {
justify-content: flex-end;
flex-basis: 20%;
text-align: right;
}
}
}
.pop-bottom {
margin-top: 0.12rem;
}
.empty {
display: flex;
align-items: center;
justify-content: center;
height: 0.5rem;
border-bottom: 0.01rem solid #e6e6e6;
color: #909399;
.iconfont {
font-size: 0.25rem;
margin: 0.05rem;
}
}
}

View File

@@ -0,0 +1,500 @@
<template>
<view class="member-detail-wrap">
<view class="member-head flex items-center justify-between">
<text>会员详情</text>
<text class="iconfont iconguanbi1 cursor-pointer" @click="$emit('close')"></text>
</view>
<view class="member-content">
<view class="content-block">
<view class="item-img">
<image mode="aspectFill" v-if="memberInfo && memberInfo.headimg" :src="$util.img(memberInfo.headimg)" @error="headError(memberInfo)"/>
<image mode="aspectFill" v-else :src="$util.img(defaultImg.head)"/>
</view>
<view class="item-content">
<view class="item-title">
<view class="item-title-text">{{ memberInfo && memberInfo.nickname ? memberInfo.nickname : '' }}</view>
<view class="item-label" v-if="memberInfo && memberInfo.member_level && memberInfo.member_level_name">{{ memberInfo.member_level_name }}</view>
</view>
<view class="info-list">
<view class="info-item">手机{{ memberInfo && memberInfo.mobile ? memberInfo.mobile : '' }}</view>
<view class="info-item" v-if="memberInfo && memberInfo.sex == 0">性别未知</view>
<view class="info-item" v-if="memberInfo && memberInfo.sex == 1">性别</view>
<view class="info-item" v-if="memberInfo && memberInfo.sex == 2">性别</view>
<view class="info-item">生日{{ memberInfo && memberInfo.birthday ? memberInfo.birthday : '' }}</view>
<view class="info-item" v-if="memberInfo && memberInfo.member_time">成为会员{{ $util.timeFormat(memberInfo.member_time) }}</view>
</view>
</view>
</view>
<view class="content-block account">
<view class="content-data-item">
<view class="data-item-title">积分</view>
<view class="data-item-value">{{ memberInfo && memberInfo.point ? parseInt(memberInfo.point) : '0' }}</view>
<view class="data-item-action" @click="memberAction('pointList')">查看</view>
</view>
<view class="content-data-item">
<view class="data-item-title">储值余额()</view>
<view class="data-item-value">{{ memberInfo && memberInfo.balance ? memberInfo.balance : '0.00' }}</view>
<view class="data-item-action" @click="memberAction('balanceList')">查看</view>
</view>
<view class="content-data-item">
<view class="data-item-title">现金余额()</view>
<view class="data-item-value">{{ memberInfo && memberInfo.balance_money ? memberInfo.balance_money : '0.00' }}</view>
</view>
<view class="content-data-item">
<view class="data-item-title">成长值</view>
<view class="data-item-value">{{ memberInfo && memberInfo.growth ? memberInfo.growth : '0' }}</view>
<view class="data-item-action" @click="memberAction('growthList')">查看</view>
</view>
<view class="content-data-item">
<view class="data-item-title">优惠券()</view>
<view class="data-item-value">{{ memberInfo && memberInfo.coupon_num ? memberInfo.coupon_num : '0' }}</view>
<view class="data-item-action" @click="memberAction('couponList')">查看</view>
</view>
<view class="content-data-item">
<view class="data-item-title">卡包</view>
<view class="data-item-value">{{ memberInfo && memberInfo.card_num ? memberInfo.card_num : '0' }}
</view>
<view class="data-item-action" @click="memberAction('cardList')">查看</view>
</view>
</view>
<view class="content-block action">
<view class="content-data-item" @click="memberAction('memberInfo')">
<view class="data-item-icon">
<image mode="aspectFit" src="@/static/member/icon-member-info.png" />
</view>
<view class="data-item-value">会员信息</view>
</view>
<view class="content-data-item" @click="memberAction('point')">
<view class="data-item-icon">
<image mode="aspectFit" src="@/static/member/icon-member-point.png" />
</view>
<view class="data-item-value">积分调整</view>
</view>
<view class="content-data-item" @click="memberAction('balance')">
<view class="data-item-icon">
<image mode="aspectFit" src="@/static/member/icon-member-balance.png" />
</view>
<view class="data-item-value">余额充值</view>
</view>
<view class="content-data-item" @click="memberAction('sendCoupon')">
<view class="data-item-icon">
<image mode="aspectFit" src="@/static/member/icon-member-coupon.png" />
</view>
<view class="data-item-value">送优惠券</view>
</view>
<view class="content-data-item" @click="memberAction('growth')">
<view class="data-item-icon">
<image mode="aspectFit" src="@/static/member/icon-member-growth.png" />
</view>
<view class="data-item-value">成长值调整</view>
</view>
<view class="content-data-item" @click="memberAction('applyMember')" v-if="memberInfo && !memberInfo.is_member">
<view class="data-item-icon">
<image mode="aspectFit" src="@/static/member/icon-member-apply.png" />
</view>
<view class="data-item-value">办理会员</view>
</view>
</view>
</view>
<!-- 会员详情 -->
<uni-popup ref="memberInfoPop">
<view class="pop-box memberInfo-box">
<view class="pop-header">
<view class="pop-header-text">会员详情</view>
<view class="pop-header-close" @click="popClose('memberInfo')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<scroll-view scroll-y="true" class="common-scrollbar pop-content">
<view class="form-content" v-if="memberInfo">
<view class="form-item">
<view class="form-label">
<text class="required"></text>
昵称
</view>
<view class="form-inline">
<input class="form-input" placeholder="请输入会员昵称" v-model="memberInfo.nickname" />
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
手机号
</view>
<view class="form-inline">
<input class="form-input" placeholder="请输入手机号" v-model="memberInfo.mobile" maxlength="11" />
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
会员等级
</view>
<view class="form-inline">
<select-lay :zindex="10" :value="memberInfo.member_level" name="names" placeholder="请选择会员等级" :options="memberLevelList" @selectitem="selectMemberLevel"/>
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
性别
</view>
<view class="form-inline">
<uni-data-checkbox v-model="memberInfo.sex" :localdata="sex"></uni-data-checkbox>
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
生日
</view>
<view class="form-inline">
<uni-datetime-picker :end="endTime" v-model="memberInfo.birthday" type="date" :clearIcon="false" />
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
注册时间
</view>
<view class="form-inline">
{{ memberInfo && memberInfo.reg_time ? $util.timeFormat(memberInfo.reg_time) : '--' }}
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
最后访问时间
</view>
<view class="form-inline">
{{ memberInfo && memberInfo.last_login_time ? $util.timeFormat(memberInfo.last_login_time) : '--' }}
</view>
</view>
</view>
</scroll-view>
<view class="pop-bottom">
<button class="primary-btn" @click="saveMemberInfo">确定</button>
</view>
</view>
</uni-popup>
<!-- 积分调整 -->
<uni-popup ref="pointPop">
<view class="pop-box pointPop-box">
<view class="pop-header">
<view class="pop-header-text">调整积分</view>
<view class="pop-header-close" @click="popClose('point')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<scroll-view scroll-y="true" class="common-scrollbar pop-content">
<view class="form-content">
<view class="form-item">
<view class="form-label">
<text class="required"></text>
当前积分
</view>
<view class="form-inline">{{ memberInfo && memberInfo.point ? memberInfo.point : '0' }}</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
调整数额
</view>
<view class="form-inline">
<input class="form-input" type="number" placeholder="请输入调整数额" v-model="pointData.num" />
<view class="word-aux">调整数额与当前积分数相加不能小于0</view>
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
备注
</view>
<view class="form-inline">
<textarea class="form-textarea" v-model="pointData.desc"/>
</view>
</view>
</view>
</scroll-view>
<view class="pop-bottom">
<button class="primary-btn" @click="savePoint">确定</button>
</view>
</view>
</uni-popup>
<!-- 发放优惠券 -->
<uni-popup ref="sendCouponPop">
<view class="pop-box sendCoupon-box">
<view class="pop-header">
<view class="pop-header-text">送优惠券</view>
<view class="pop-header-close" @click="popClose('sendCoupon')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<view class="common-scrollbar sendCoupon-content">
<view class="coupon-table-head">
<view class="coupon-table-th">优惠券名称</view>
<view class="coupon-table-th">金额</view>
<view class="coupon-table-th">有效期</view>
<view class="coupon-table-th">发放数量</view>
</view>
<scroll-view class="coupon-table-body" @scrolltolower="getCouponList()" scroll-y="true">
<view class="coupon-table-tr" v-for="(item, index) in sendCoupon.list" :key="index">
<view class="coupon-table-td">{{ item.coupon_name }}</view>
<view class="coupon-table-td">{{ item.money }}</view>
<view class="coupon-table-td">{{ item.validity_name }}</view>
<view class="coupon-table-td">
<view class="item-num">
<view class="num-dec" v-on:click="dec(item)">-</view>
<input class="table-input" type="text" v-model="item.num" />
<view class="num-inc" v-on:click="inc(item)">+</view>
</view>
</view>
</view>
<view class="empty" v-if="!sendCoupon.list.length">
<view class="iconfont iconwushuju"></view>
<view>暂无数据</view>
</view>
</scroll-view>
</view>
<view class="pop-bottom">
<button v-if="sendCoupon.list.length" class="primary-btn" @click="sendCouponFn">发放优惠券</button>
</view>
</view>
</uni-popup>
<!-- 余额调整 -->
<uni-popup ref="balancePop">
<view class="pop-box pointPop-box">
<view class="pop-header">
<view class="pop-header-text">调整余额</view>
<view class="pop-header-close" @click="popClose('balance')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<scroll-view scroll-y="true" class="common-scrollbar pop-content">
<view class="form-content">
<view class="form-item">
<view class="form-label">
<text class="required"></text>
当前余额
</view>
<view class="form-inline">
{{ memberInfo && memberInfo.balance ? memberInfo.balance : '0.00' }}
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
调整数额
</view>
<view class="form-inline">
<input class="form-input" type="number" placeholder="请输入调整数额" v-model="balanceData.num" />
<view class="word-aux">调整数额与当前储值余额相加不能小于0</view>
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
备注
</view>
<view class="form-inline">
<textarea class="form-textarea" v-model="balanceData.desc"></textarea>
</view>
</view>
</view>
</scroll-view>
<view class="pop-bottom">
<button class="primary-btn" @click="saveBalance">确定</button>
</view>
</view>
</uni-popup>
<!-- 成长值调整 -->
<uni-popup ref="growthPop">
<view class="pop-box pointPop-box">
<view class="pop-header">
<view class="pop-header-text">调整成长值</view>
<view class="pop-header-close" @click="popClose('growth')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<scroll-view scroll-y="true" class="common-scrollbar pop-content">
<view class="form-content">
<view class="form-item">
<view class="form-label">
<text class="required"></text>
当前成长值
</view>
<view class="form-inline">{{ memberInfo && memberInfo.growth ? memberInfo.growth : '0' }}</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
调整数额
</view>
<view class="form-inline">
<input class="form-input" type="number" placeholder="请输入调整数额" v-model="growthData.num" />
<view class="word-aux">调整数额与当前成长值相加不能小于0</view>
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
备注
</view>
<view class="form-inline">
<textarea class="form-textarea" v-model="growthData.desc"></textarea>
</view>
</view>
</view>
</scroll-view>
<view class="pop-bottom">
<button class="primary-btn" @click="saveGrowth">确定</button>
</view>
</view>
</uni-popup>
<!-- 办理会员 -->
<uni-popup ref="applyMemberPop">
<view class="pop-box applyMemberPop-box">
<view class="pop-header">
<view class="pop-header-text">办理会员</view>
<view class="pop-header-close" @click="popClose('applyMember')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<view class="common-scrollbar pop-content">
<view class="form-content">
<view class="form-item">
<view class="form-label">
<text class="required"></text>
会员等级
</view>
<view class="form-inline">
<select-lay :zindex="10" :value="applyMember.level_id" name="names" placeholder="请选择会员等级" :options="memberLevelList" @selectitem="selectMemberLevel"/>
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
会员卡号
</view>
<view class="form-inline">
<input class="form-input" type="text" placeholder="请输入会员卡号" v-model="applyMember.member_code" />
<view class="word-aux">会员卡号为会员唯一编号若不设置将会自动生成</view>
</view>
</view>
</view>
</view>
<view class="pop-bottom">
<button class="primary-btn" @click="saveApplyMember">确定</button>
</view>
</view>
</uni-popup>
<!-- 优惠券列表 -->
<uni-popup ref="couponListPop">
<view class="pop-box coupon-list-pop-box">
<view class="pop-header">
<view class="pop-header-text">优惠券</view>
<view class="pop-header-close" @click="popClose('couponList')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<scroll-view scroll-y="true" class="common-scrollbar pop-content">
<dataTable url="/cashier/storeapi/member/coupon" :cols="couponCols" ref="table" :option="option" :pagesize="pageSize"></dataTable>
</scroll-view>
</view>
</uni-popup>
<!-- 积分列表 -->
<uni-popup ref="pointListPop">
<view class="pop-box coupon-list-pop-box">
<view class="pop-header">
<view class="pop-header-text">积分</view>
<view class="pop-header-close" @click="popClose('pointList')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<scroll-view scroll-y="true" class="common-scrollbar pop-content">
<dataTable url="/cashier/storeapi/member/memberaccountlist" :cols="pointCols" ref="table" :option="option" :pagesize="pageSize"></dataTable>
</scroll-view>
</view>
</uni-popup>
<!-- 余额列表 -->
<uni-popup ref="balanceListPop">
<view class="pop-box coupon-list-pop-box">
<view class="pop-header">
<view class="pop-header-text">余额</view>
<view class="pop-header-close" @click="popClose('balanceList')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<scroll-view scroll-y="true" class="common-scrollbar pop-content">
<dataTable url="/cashier/storeapi/member/memberaccountlist" :cols="balanceCols" ref="table" :option="option" :pagesize="pageSize"></dataTable>
</scroll-view>
</view>
</uni-popup>
<!-- 成长值列表 -->
<uni-popup ref="growthListPop">
<view class="pop-box coupon-list-pop-box">
<view class="pop-header">
<view class="pop-header-text">成长值</view>
<view class="pop-header-close" @click="popClose('growthList')">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<scroll-view scroll-y="true" class="common-scrollbar pop-content">
<dataTable url="/cashier/storeapi/member/memberaccountlist" :cols="growthCols" ref="table" :option="option" :pagesize="pageSize"></dataTable>
</scroll-view>
</view>
</uni-popup>
<!-- 卡项 -->
<ns-member-card-record ref="memberCardRecord" :option="option"/>
</view>
</template>
<script>
import dataTable from '@/components/uni-data-table/uni-data-table.vue';
import nsMemberCardRecord from '@/components/ns-member-card-record/ns-member-card-record.vue';
import index from './index.js';
export default {
components: {
dataTable,
nsMemberCardRecord
},
mixins: [index]
};
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

View File

@@ -0,0 +1,106 @@
<template>
<view class="journal">
<view class="item" v-for="(item, index) in list" :key="index">
<view class="time">
<view>{{ $util.timeFormat(item.action_time).split(' ')[0] }}</view>
<view>{{ $util.timeFormat(item.action_time).split(' ')[1] }}</view>
</view>
<view class="unit">
<view class="top">
<view class="core"></view>
<view class="unit-separate"></view>
</view>
</view>
<view class="message">{{ item.action }}</view>
</view>
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default: function() {
return [];
}
}
},
data() {
return {};
},
mounted() {},
methods: {}
};
</script>
<style scoped lang="scss">
.journal {
padding-left: 0.1rem;
box-sizing: border-box;
.item {
width: 100%;
height: 0.7rem;
display: flex;
.time {
margin-right: 0.1rem;
min-width: 1rem;
view:nth-child(1) {
font-size: 0.16rem;
margin-bottom: 0.1rem;
text-align: right;
}
view:nth-child(2) {
font-size: 0.14rem;
color: #999999;
text-align: right;
}
}
}
.unit {
width: 0.18rem;
height: 100%;
margin-right: 0.1rem;
.top {
width: 0.18rem;
height: 0.18rem;
border-radius: 50%;
background: $primary-color;
position: relative;
.core {
background: #ffffff;
width: 0.08rem;
height: 0.08rem;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 999;
}
.unit-separate {
position: absolute;
width: 0.01rem;
height: 0.7rem;
top: 0;
left: 50%;
transform: translateX(-50%);
background: $primary-color;
z-index: 555;
}
}
}
.message {
font-size: 0.14rem;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,786 @@
.container {
width: 100%;
height: 100%;
& > view {
width: 100%;
height: 100%;
}
}
.payment-wrap {
.header {
height: 0.66rem;
display: flex;
align-items: center;
border-bottom: 0.01rem solid #e6e6e6;
}
.body {
flex: 1;
height: 0;
padding: 0.15rem 0;
box-sizing: border-box;
display: flex;
}
.info-wrap {
flex: 1;
width: 0;
margin-right: 0.15rem;
display: flex;
flex-direction: column;
.info {
flex: 1;
height: 0;
background-color: #f7f8fa;
padding-bottom: 0.15rem;
box-sizing: border-box;
/deep/ .uni-scroll-view-content {
margin: 0 0.15rem;
width: calc(100% - 0.3rem);
box-sizing: border-box;
}
.payment-money {
text-align: right;
font-size: 0.2rem;
border-bottom: 0.01rem solid #e6e6e6;
line-height: 0.6rem;
}
.title {
line-height: 0.6rem;
font-size: 0.16rem;
}
.uni-flex {
flex-wrap: wrap;
}
.type-item {
padding: 0.2rem 0.1rem;
background: #fff;
border: 0.01rem solid #e6e6e6;
display: flex;
align-items: center;
font-size: 0.16rem;
margin: 0 0.1rem 0.1rem 0;
width: calc((100% - 0.86rem) / 3);
line-height: 1;
cursor: pointer;
position: relative;
border-radius: 0.02rem;
&.account {
width: calc((100% - 0.86rem) / 2);
}
.name {
flex: 1;
width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&:nth-child(3n + 3) {
margin-right: 0;
}
.iconfont {
color: $primary-color;
font-size: 0.3rem;
margin-right: 0.1rem;
}
.text {
color: #fe2278;
margin-left: 0.05rem;
}
.iconxuanzhong {
position: absolute;
display: none;
}
&.active {
border-color: $primary-color;
.iconxuanzhong {
display: block;
right: -0.11rem;
bottom: -0.01rem;
}
}
&.disabled {
background: #f5f5f5;
cursor: not-allowed;
}
}
.pay-type {
.type-item {
padding: 0.15rem 0.1rem;
}
.pay-icon {
color: #fff;
background: #f0f0f0;
width: 0.3rem;
height: 0.3rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.16rem;
border-radius: 0.05rem;
}
}
}
.button-wrap {
padding-top: 0.15rem;
display: flex;
justify-content: flex-end;
align-items: center;
.scancode {
color: $primary-color;
}
button {
margin: 0 0 0 0.1rem;
min-width: 1rem;
}
.print-ticket {
flex: 1;
width: 0;
display: flex;
align-items: center;
}
}
}
.bill-wrap {
width: 3rem;
border: 0.01rem solid #e6e6e6;
.title {
text-align: center;
font-size: 0.2rem;
border-bottom: 0.01rem solid #e6e6e6;
line-height: 0.6rem;
}
.body {
padding: 0;
margin: 0 0.15rem;
display: block;
height: auto;
.block-title {
position: relative;
text-align: center;
width: 100%;
height: 0.35rem;
margin-top: 0.2rem;
text {
padding: 0 0.2rem;
background: #fff;
position: absolute;
left: 50%;
top: 50%;
z-index: 1;
transform: translate(-50%, -50%);
font-size: 0.16rem;
}
&::before {
content: '';
position: absolute;
width: 100%;
top: 50%;
left: 0;
border-top: 0.01rem dashed #e6e6e6;
}
}
.bill-info {
display: flex;
justify-content: space-between;
line-height: 1;
align-items: center;
margin-top: 0.2rem;
.text {
color: #fe2278;
}
}
}
}
.remark-info {
padding: 0.1rem;
background-color: var(--primary-color-light-9);
color: $primary-color;
margin-top: 0.1rem;
font-size: 0.12rem;
}
}
.pay-result {
.body {
flex: 1;
height: 0;
text-align: center;
&.status {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
.iconfont {
font-size: 1rem;
color: $primary-color;
}
.msg {
margin-top: 0.1rem;
font-size: 0.16rem;
color: $primary-color;
}
}
}
.footer {
height: 0.66rem;
display: flex;
align-items: center;
border-top: 0.01rem solid #e6e6e6;
justify-content: center;
button {
margin: 0 0 0 0.15rem;
width: auto;
min-width: 1.6rem;
height: 0.45rem;
line-height: 0.45rem;
}
}
}
.money-wrap {
background: #fff;
border-radius: 0.05rem;
.head {
height: 0.6rem;
line-height: 0.6rem;
text-align: center;
font-weight: bold;
position: relative;
text {
font-size: 0.16rem;
}
.iconguanbi1 {
position: absolute;
right: 0.15rem;
font-size: 0.22rem;
cursor: pointer;
}
}
.content-wrap {
display: flex;
border: 0.01rem solid #e6e6e6;
height: 0.6rem;
align-items: center;
margin: 0 0.2rem;
padding: 0 0.15rem;
.unit {
font-size: 0.25rem;
}
.money {
margin-left: 0.05rem;
font-size: 0.2rem;
}
}
.keyboard-wrap {
width: 4rem;
padding: 0 0.2rem 0.3rem 0.2rem;
margin-top: 0.1rem;
}
}
.coupon-wrap {
background: #fff;
width: 6rem;
border-radius: 0.05rem;
.head {
height: 0.6rem;
line-height: 0.6rem;
text-align: center;
font-weight: bold;
position: relative;
text {
font-size: 0.16rem;
}
.iconguanbi1 {
position: absolute;
right: 0.15rem;
font-size: 0.22rem;
cursor: pointer;
}
}
.body {
height: 3rem;
}
.list {
display: flex;
padding: 0.1rem 0.15rem;
flex-wrap: wrap;
.item {
margin: 0 0.1rem 0.1rem 0;
padding: 0.1rem 0;
border: 0.01rem solid #e6e6e6;
width: calc((100% - 0.14rem) / 2);
cursor: pointer;
display: flex;
position: relative;
.iconxuanzhong {
position: absolute;
display: none;
right: -0.01rem;
bottom: -0.01rem;
font-size: 0.3rem;
}
&.active {
border-color: $primary-color;
.iconxuanzhong {
display: block;
color: $primary-color;
}
}
&:nth-child(2n + 2) {
margin-right: 0;
}
.money {
display: flex;
align-items: center;
justify-content: center;
min-height: 0.6rem;
min-width: 1rem;
font-size: 0.2rem;
line-height: 1;
.unit {
font-size: 0.16rem;
margin-top: 0.05rem;
font-weight: bold;
}
}
.info {
padding: 0 0.1rem;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
.title {
font-weight: bold;
}
.time,
.limit {
font-size: 0.12rem;
color: #999;
line-height: 1;
margin-top: 0.05rem;
}
}
}
}
}
.safe-verify-popup {
width: 4.4rem;
height: 3.1rem;
background-color: #fff;
border-radius: 0.1rem;
.header {
height: 0.6rem;
line-height: 0.6rem;
text-align: center;
position: relative;
.type-wrap {
display: flex;
.item {
margin-left: 0.15rem;
font-size: 0.16rem;
cursor: pointer;
&.active {
font-size: 0.18rem;
color: $primary-color;
font-weight: bold;
}
}
}
.iconguanbi1 {
position: absolute;
right: 0.15rem;
top: 0;
font-size: 0.22rem;
cursor: pointer;
font-weight: bold;
}
}
.content {
padding: 0 0.3rem;
margin-top: 0.2rem;
}
.member-code-hint{
margin-top: .3rem;
font-size: .16rem;
}
.tips {
color: #999;
}
.mobile {
font-size: 0.25rem;
font-weight: bold;
margin-top: 0.05rem;
}
.sms-code {
display: flex;
align-items: center;
margin-top: 0.15rem;
border-bottom: 0.01rem solid #eee;
padding: 0.15rem 0;
view {
position: relative;
display: flex;
align-items: center;
flex: 1;
input {
flex: 1;
margin: 0 0.1rem 0 0;
padding: 0;
border-bottom: none;
font-size: 0.14rem;
}
text {
position: absolute;
right: 0.1rem;
font-size: 0.2rem;
color: #999;
cursor: pointer;
}
}
.send-tip {
color: $primary-color;
font-size: 0.13rem;
cursor: pointer;
&.disabled {
color: #999;
cursor: not-allowed;
}
}
}
.placeholder {
font-size: 0.14rem;
}
.primary-btn {
margin-top: 0.3rem;
line-height: 0.4rem;
}
.scancode-wrap {
text-align: center;
.input-wrap {
display: flex;
view {
position: relative;
display: flex;
align-items: center;
flex: 1;
input {
width: 0;
flex: 1;
height: 0.5rem;
border: 0.01rem solid #cccccc;
text-align: center;
padding: 0 0.1rem;
box-sizing: border-box;
transition: all 0.3s;
&.focus {
border-color: $primary-color;
box-shadow: 0 0 0.02rem 0.02rem var(--primary-color-light-7);
}
}
text {
position: absolute;
right: 0.1rem;
font-size: 0.2rem;
color: #999;
cursor: pointer;
}
}
.primary-btn {
margin: 0 0 0 0.1rem;
line-height: 0.5rem;
width: 1rem;
padding: 0;
}
}
image {
width: 3rem;
padding: 0.2rem 0.4rem;
box-sizing: border-box;
}
}
}
.third-popup {
width: 4rem;
height: 5rem;
background-color: #fff;
border-radius: 0.1rem;
display: flex;
flex-direction: column;
.head {
height: 0.8rem;
line-height: 0.8rem;
text-align: center;
position: relative;
text {
font-size: 0.25rem;
}
.iconguanbi1 {
position: absolute;
right: 0.15rem;
font-size: 0.22rem;
cursor: pointer;
font-weight: bold;
}
}
.money {
text-align: center;
font-size: 0.18rem;
color: $primary-color;
}
.scan-code-type {
display: flex;
width: 100%;
margin-top: 0.2rem;
background-color: #f5f5f5;
.type-item {
flex: 1;
text-align: center;
line-height: 0.5rem;
font-size: 0.16rem;
cursor: pointer;
border-bottom: 0.03rem solid #f5f5f5;
position: relative;
&.active {
border-bottom: 0.03rem solid $primary-color;
}
&:last-child::after {
content: ' ';
position: absolute;
left: 0;
top: 20%;
width: 0.01rem;
height: 60%;
background: #ddd;
}
}
}
.content-wrap {
flex: 1;
height: 0;
display: flex;
align-items: center;
justify-content: center;
.qrcode-wrap {
display: flex;
justify-content: center;
.empty {
padding: 1rem 0;
text-align: center;
}
.qrcode-item {
height: 1.3rem;
width: 1.3rem;
padding: 0.1rem;
box-shadow: 0 0.02rem 0.1rem 0 rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
position: relative;
.qrcode {
width: 100%;
}
.logo {
width: 0.25rem;
position: absolute !important;
z-index: 5;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
&:nth-child(2) {
margin-left: 0.2rem;
}
}
}
.scancode-wrap {
view {
position: relative;
display: flex;
align-items: center;
input {
width: 3.3rem;
height: 0.5rem;
border: 0.01rem solid #cccccc;
text-align: center;
padding: 0 0.1rem;
box-sizing: border-box;
transition: all 0.3s;
&.focus {
border-color: $primary-color;
box-shadow: 0 0 0.02rem 0.02rem var(--primary-color-light-7);
}
}
text {
position: absolute;
right: 0.1rem;
font-size: 0.2rem;
color: #999;
cursor: pointer;
}
}
image {
width: 3.3rem;
padding: 0.2rem 0.4rem;
box-sizing: border-box;
}
}
}
}
.remark-wrap {
width: 6rem;
background-color: #fff;
border-radius: 0.04rem;
box-shadow: 0 0.01rem 0.12rem 0 rgba(0, 0, 0, 0.1);
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 0.15rem;
height: 0.45rem;
line-height: 0.45rem;
border-bottom: 0.01rem solid #e8eaec;
.iconfont {
font-size: $uni-font-size-lg;
}
}
.body {
padding: 0.15rem 0.15rem 0.1rem;
textarea {
border: 0.01rem solid #e6e6e6;
width: 100%;
padding: 0.1rem;
box-sizing: border-box;
font-size: 0.14rem;
}
.placeholder-class {
font-size: 0.14rem;
}
}
.footer {
height: 0.5rem;
padding-bottom: 0.05rem;
display: flex;
align-items: center;
justify-content: center;
button {
width: 95%;
}
}
}

View File

@@ -0,0 +1,366 @@
<template>
<view class="container" v-if="payInfo">
<view class="uni-flex uni-column payment-wrap" v-show="payStatus == 'pay'">
<view class="header">结算</view>
<view class="body">
<view class="info-wrap">
<scroll-view scroll-y="true" class="info">
<view class="payment-money">费用总额{{ payInfo.original_money | moneyFormat }}</view>
<block v-if="promotionShow">
<view class="title">营销优惠</view>
<view class="uni-flex">
<view class="type-item" :class="{ disabled: payInfo.offset.coupon_array.member_coupon_list.length == 0, active: discount.coupon_id }" @click="selectCoupon" v-if="payInfo.offset.coupon_array">
<view class="iconfont iconyouhuiquan"></view>
<view class="name" v-show="!discount.coupon_id">
优惠券
<text class="text" v-if="payInfo.offset.coupon_array.member_coupon_list.length">
{{ payInfo.offset.coupon_array.member_coupon_list.length }}张可用
</text>
</view>
<view class="name" v-show="discount.coupon_id">
优惠券抵扣
<text class="text">{{ payInfo.coupon_money }}</text>
</view>
<view class="iconfont iconxuanzhong"></view>
</view>
<view class="type-item" :class="{ active: discount.reduction }" @click="reduction" v-if="payInfo.collectmoney_config.reduction == 1">
<view class="iconfont iconjianmianjine"></view>
<view class="name" v-if="discount.reduction" @click.stop="openMoneyPopup({ title: '减免金额', money: $util.moneyFormat(discount.reduction), type: 'reduction' })">
减免
<text class="text">{{ discount.reduction }}</text>
</view>
<view v-else class="name">减免金额</view>
<view class="iconfont iconxuanzhong"></view>
</view>
</view>
</block>
<block v-if="payInfo.offset.point_array || payInfo.offset.balance">
<view class="title">账户余额</view>
<view class="uni-flex">
<view class="type-item account" :class="{ active: discount.is_use_balance, disabled: balance == 0 }" @click="useBalance" v-if="payInfo.offset.balance">
<view class="iconfont iconyue"></view>
<view class="name" v-if="discount.is_use_balance">
余额支付
<text class="text">{{ payInfo.total_balance | moneyFormat }}</text>
</view>
<view class="name" v-else>
账户余额
<text class="text" v-if="balance > 0">{{ balance | moneyFormat }}</text>
</view>
<view class="iconfont iconxuanzhong"></view>
</view>
<view class="type-item account" :class="{ active: discount.is_use_point, disabled: payInfo.offset.point_array.point == 0 }" @click="usePoint" v-if="payInfo.offset.point_array">
<view class="iconfont iconjifen1"></view>
<view class="name" v-if="discount.is_use_point">
积分抵扣
<text class="text">{{ payInfo.point_money | moneyFormat }}{{ parseInt(payInfo.offset.point_array.point) }}积分</text>
</view>
<view class="name" v-else>
账户积分
<text class="text" v-if="globalMemberInfo.point">{{ globalMemberInfo.point }}积分</text>
</view>
<view class="iconfont iconxuanzhong"></view>
</view>
</view>
</block>
<view class="title">支付方式</view>
<view class="uni-flex pay-type">
<block v-for="(item, key,index) in payType" :key="key">
<view class="type-item" @click="switchPayType(item.type)" :class="{ active: item.type == type }">
<view class="pay-icon iconfont" :style="{ background: item.background }" :class="item.icon"></view>
<view class="name">{{ item.name }} [{{ item.hotKey }}]</view>
<view class="iconfont iconxuanzhong"></view>
</view>
</block>
<view class="type-item" @click="switchMemberCode()" :class="{ active: discount.is_use_balance}">
<view class="pay-icon iconfont iconhuiyuanma" :style="{ background: '#F7861E' }"></view>
<view class="name">
<text>会员码 [M]</text>
<template v-if="discount.is_use_balance">
<text style="margin-left: 0.05rem;">(</text>
<text style="margin-left: 0.05rem;">使用余额</text>
<text class="text">{{ payInfo.total_balance | moneyFormat }}</text>
<text style="margin-left: 0.05rem;">)</text>
</template>
</view>
<!-- <view class="iconfont iconxuanzhong"></view> -->
</view>
</view>
<view class="remark-info" v-if="payInfo.remark">备注{{ payInfo.remark }}</view>
</scroll-view>
<view class="button-wrap">
<view class="print-ticket">
<checkbox-group @change="autoPrintTicket = !autoPrintTicket">
<label>
<checkbox :checked="autoPrintTicket" style="transform:scale(0.7)" />
<text>打印小票</text>
</label>
</checkbox-group>
</view>
<button class="default-btn" @click="openRemark">备注</button>
<button class="default-btn cancel-btn" plain @click="cancelPayment">取消 [Esc]</button>
<button class="primary-btn" @click="confirm()" v-if="type != 'third' || payInfo.pay_money == 0">收款 [Enter]</button>
<button class="primary-btn" @click="thirdConfirm()" v-else>收款 [Enter]</button>
</view>
</view>
<scroll-view scroll-y="true" class="bill-wrap">
<view class="title">支付明细</view>
<view class="body">
<view class="bill-info">
<view>费用总额</view>
<view>{{ payInfo.original_money | moneyFormat }}</view>
</view>
<view class="block-title"><text>营销优惠</text></view>
<view class="bill-info">
<view>减免金额</view>
<view class="text">
-{{ payInfo.offset.reduction ? $util.moneyFormat(payInfo.offset.reduction) : '0.00' }}
</view>
</view>
<view class="bill-info" v-if="payInfo.offset.coupon_array">
<view>优惠券</view>
<view class="text">-{{ $util.moneyFormat(payInfo.coupon_money) }}</view>
</view>
<view class="bill-info" v-if="payInfo.offset.hongbao_array">
<view>红包</view>
<view class="text">-{{ $util.moneyFormat(payInfo.hongbao_money) }}</view>
</view>
<view class="bill-info" v-if="payInfo.offset.point_array">
<view>积分抵扣</view>
<view class="text">-{{ $util.moneyFormat(payInfo.point_money) }}</view>
</view>
<block v-if="payInfo.offset.balance">
<view class="block-title"><text>余额抵扣</text></view>
<view class="bill-info">
<view>余额支付</view>
<view>-{{ $util.moneyFormat(payInfo.total_balance) }}</view>
</view>
</block>
<view class="block-title"><text>支付方式</text></view>
<view class="bill-info">
<view>{{ payType[type].name }}</view>
<view v-show="type == 'cash'">
{{ payInfo.cash > 0 ? $util.moneyFormat(payInfo.cash) : $util.moneyFormat(payInfo.pay_money) }}
</view>
<view v-show="type != 'cash'">{{ payInfo.pay_money | moneyFormat }}</view>
</view>
<view class="block-title"></view>
<view class="bill-info">
<view>需支付</view>
<view>{{ payInfo.pay_money | moneyFormat }}</view>
</view>
<view class="bill-info">
<view>实付</view>
<view v-show="type == 'cash'">
{{ payInfo.cash > 0 ? $util.moneyFormat(payInfo.cash) : $util.moneyFormat(payInfo.pay_money) }}
</view>
<view v-show="type != 'cash'">{{ payInfo.pay_money | moneyFormat }}</view>
</view>
<view class="bill-info" v-if="payInfo.cash_change > 0">
<view>找零</view>
<view>{{ payInfo.cash_change | moneyFormat }}</view>
</view>
</view>
</scroll-view>
</view>
</view>
<!-- 支付结果 -->
<view class="uni-flex uni-column pay-result" v-show="payStatus == 'success'">
<view class="body status">
<view class="iconfont iconchenggong"></view>
<view class="msg">收款成功</view>
</view>
<view class="footer">
<button class="primary-btn" @click="paySuccess">继续收款 [Enter]{{ autoComplete.time }}s</button>
</view>
</view>
<uni-popup ref="moneyPopup" type="center">
<view class="money-wrap">
<view class="head">
<text>{{ moneyPopup.title }}</text>
<text class="iconfont iconguanbi1" @click="$refs.moneyPopup.close()"></text>
</view>
<view class="content-wrap">
<view class="unit"></view>
<view class="money">{{ moneyPopup.money }}</view>
</view>
<view class="keyboard-wrap">
<view class="num-wrap">
<view class="key-item" @click="keydown('1')">1</view>
<view class="key-item" @click="keydown('2')">2</view>
<view class="key-item" @click="keydown('3')">3</view>
<view class="key-item" @click="keydown('4')">4</view>
<view class="key-item" @click="keydown('5')">5</view>
<view class="key-item" @click="keydown('6')">6</view>
<view class="key-item" @click="keydown('7')">7</view>
<view class="key-item" @click="keydown('8')">8</view>
<view class="key-item" @click="keydown('9')">9</view>
<view class="key-item" @click="keydown('00')">00</view>
<view class="key-item" @click="keydown('0')">0</view>
<view class="key-item" @click="keydown('.')">.</view>
</view>
<view class="action-wrap">
<view class="delete" @click="deleteCode">删除</view>
<view class="delete" @click="moneyPopup.money = ''">清空</view>
<view class="confirm" @click="moneyPopupConfirm()">确认</view>
</view>
</view>
</view>
</uni-popup>
<uni-popup ref="couponPopup" type="center" v-if="payInfo.offset.coupon_array && payInfo.offset.coupon_array.member_coupon_list.length">
<view class="coupon-wrap">
<view class="head">
<text>选择优惠券</text>
<text class="iconfont iconguanbi1" @click="$refs.couponPopup.close()"></text>
</view>
<scroll-view scroll-y="true" class="body">
<view class="list">
<view class="item" :class="{ active: discount.coupon_id && discount.coupon_id == item.coupon_id }" v-for="(item, index) in payInfo.offset.coupon_array.member_coupon_list" :key="index" @click="selectCouponItem(item)">
<view class="money" v-show="item.type == 'discount'">
{{ item.discount }}
<text class="unit"></text>
</view>
<view class="money" v-show="item.type != 'discount'">
<text class="unit"></text>
{{ item.money }}
</view>
<view class="info">
<view class="title">{{ item.coupon_name }}</view>
<view class="limit">
{{ item.at_least == 0 ? '无门槛券' : '满' + item.at_least + '可用' }}
{{ item.type == 'discount' && item.discount_limit > 0 ? ',最多优惠' + item.discount_limit : '' }}
</view>
<view class="time" v-if="item.end_time">{{ $util.timeFormat(item.end_time, 'y-m-d') }}前可用
</view>
<view class="time" v-else>长期有效</view>
</view>
<view class="iconfont iconxuanzhong"></view>
</view>
</view>
</scroll-view>
</view>
</uni-popup>
<!-- 扫码枪支付弹窗 -->
<uni-popup ref="thirdPopup" type="center" @change="popupChange">
<view class="third-popup">
<view class="head">
<text>请选择扫码方式</text>
<text class="iconfont iconguanbi1" @click="$refs.thirdPopup.close();thirdPopupOpen = false;"></text>
</view>
<view class="money">扫码收款{{ payInfo.pay_money | moneyFormat }}</view>
<view class="scan-code-type" v-if="type == 'third'">
<view class="type-item" :class="{ active: scanCodeType == 'scancode' }" @click="scanCodeType = 'scancode'">扫码枪</view>
<view class="type-item" :class="{ active: scanCodeType == 'qrcode' }" @click="scanCodeType = 'qrcode'">二维码</view>
</view>
<view class="content-wrap">
<view class="qrcode-wrap" v-show="scanCodeType == 'qrcode'">
<block v-if="payQrcode.length">
<view class="qrcode-item" v-for="(item, index) in payQrcode" :key="index">
<image :src="item.qrcode.replace(/[\r\n]/g, '')" mode="widthFix" class="qrcode" v-if="item.qrcode.indexOf('data:image') != -1" />
<image :src="$util.img(item.qrcode)" mode="widthFix" class="qrcode" v-else />
<image :src="$util.img(item.logo)" mode="widthFix" class="logo" />
</view>
</block>
<view class="empty" v-else>没有可用的收款二维码</view>
</view>
<view class="scancode-wrap" v-show="scanCodeType == 'scancode'">
<block v-if="scancodeList.length">
<view>
<input type="number" v-model="authCode" :class="{ focus: scanCodeFocus }"
:focus="scanCodeFocus" placeholder="请点击输入框聚焦扫码或输入付款码" @confirm="scanCode"
@focus="scanCodeFocus = true" @blur="scanCodeInputBlur()" />
<text class="iconfont icondelete" v-show="authCode.length > 0" @click="clearAuthCode"></text>
</view>
<image src="@/static/cashier/scan_code_tip.png" mode="widthFix" />
</block>
<view class="empty" v-else>没有可用的支付方式</view>
</view>
</view>
</view>
</uni-popup>
<!-- 使用账号余额验证会员码/手机号 -->
<uni-popup ref="safeVerifyPopup" type="center">
<view class="safe-verify-popup">
<view class="header">
<view class="type-wrap" v-if="active == 'memberCodePopup'">
<view class="item">会员码</view>
</view>
<view class="type-wrap" v-else-if="active == 'safeVerifyPopup' && payInfo.collectmoney_config.sms_verify == 1">
<view class="item" :class="{ active: safeVerifyType == 'payment_code' }" @click="changeSafeVerifyType('payment_code')">会员码</view>
<view class="item" :class="{ active: safeVerifyType == 'sms_code' }" @click="changeSafeVerifyType('sms_code')">短信验证码</view>
</view>
<text class="iconfont iconguanbi1" @click="$refs.safeVerifyPopup.close()"></text>
</view>
<view class="content" v-show="safeVerifyType == 'payment_code'">
<view class="scancode-wrap">
<view class="input-wrap">
<view>
<input type="number" v-model="paymentCode" :class="{ focus: scanCodeFocus }"
:focus="scanCodeFocus" placeholder="请点击输入框聚焦扫码或输入会员码" @confirm="verifyPaymentCode"
@focus="scanCodeFocus = true" @blur="scanCodeInputBlur()"
placeholder-class="placeholder" />
<text class="iconfont icondelete" v-show="paymentCode.length > 0" @click="clearPaymentCode"></text>
</view>
<button class="primary-btn" @click="verifyPaymentCode">确认</button>
</view>
<image src="@/static/cashier/scan_code_tip.png" mode="widthFix" />
<!-- <view class="member-code-hint">打开手机端 --个人中心 -- 会员码</view> -->
</view>
</view>
<view class="content" v-show="safeVerifyType == 'sms_code' && active == 'safeVerifyPopup'">
<block v-if="payInfo.member_account">
<view class="tip">将发送验证码到该手机</view>
<view class="mobile">
{{ payInfo.member_account.mobile.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2') }}
</view>
<view class="sms-code">
<view>
<input type="number" v-model="smsCode" class="sms-code" placeholder="请输入验证码"
:focus="scanCodeFocus" placeholder-class="placeholder" @focus="scanCodeFocus = true"
@blur="scanCodeFocus = false" />
<text class="iconfont icondelete" v-show="smsCode.length > 0" @click="clearSmsCode"></text>
</view>
<text class="send-tip" @click="sendMobileCode" :class="{ disabled: dynacodeData.isSend }">{{ dynacodeData.codeText }}</text>
</view>
<button class="primary-btn" @click="verifySmsCode">确认</button>
</block>
<view v-else>该会员尚未绑定手机号无法使用该验证方式</view>
</view>
</view>
</uni-popup>
<uni-popup ref="remarkPopup" type="center">
<view class="remark-wrap">
<view class="header">
<text class="title">备注</text>
<text class="iconfont iconguanbi1" @click="$refs.remarkPopup.close()"></text>
</view>
<view class="body">
<textarea v-model="remark" placeholder="填写备注信息" placeholder-class="placeholder-class" @keydown.enter="remarkConfirm" />
</view>
<view class="footer">
<button type="default" class="primary-btn" @click="remarkConfirm">确认</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import index from './index.js';
export default {
name: 'nsPayment',
mixins: [index]
};
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

View File

@@ -0,0 +1,146 @@
import { editPendOrderRemark, deletePendOrder, getPendOrderList } from '@/api/pendorder.js';
import { getMemberInfoById } from '@/api/member.js'
import {mapGetters} from 'vuex';
export default {
name: 'nsPendOrder',
data() {
return {
orderData: {
page: 0,
total: 1,
list: []
},
remark: '',
index: -1,
orderId: 0,
isRepeat: false,
height: ''
};
},
computed: {
...mapGetters(['pendOrderNum'])
},
created() {
this.getOrder();
},
mounted() {
this.setHeight();
},
methods: {
open() {
this.$refs.pendOrderPop.open();
},
getOrder(page = null) {
if (page === 0) this.orderData.page = page;
this.orderId = 0;
if (this.orderData.page + 1 > this.orderData.total) return;
this.orderData.page++;
getPendOrderList({ page: this.orderData.page }).then(res => {
if (res.code == 0) {
if (this.orderData.page == 1) this.orderData.list = [];
this.$store.commit('billing/setPendOrderNum', res.data.count);
if (res.data.list.length) {
this.orderData.total = res.data.page_count;
this.orderData.list = this.orderData.list.concat(res.data.list);
} else {
this.orderData.total = 1;
}
this.setHeight();
}
});
},
deleteOrder(order_id) {
if (this.isRepeat) return;
this.isRepeat = true;
deletePendOrder(order_id).then(res => {
if (res.code == 0) {
this.isRepeat = false;
this.getOrder(0);
}
});
},
remarkConfirm() {
let data = this.orderData.list[this.index];
editPendOrderRemark({
order_id: data.order_id,
remark: this.remark
}).then(res => {
if (res.code == 0) {
this.orderData.list[this.index].remark = this.remark;
this.$refs.remarkPopup.close();
} else {
this.$util.showToast({
title: '操作失败'
});
}
})
},
remarkSetting(data, index) {
this.index = index;
this.remark = data.remark;
this.$refs.remarkPopup.open();
},
async takeOrder(data) {
this.orderId = data.order_id;
//获取挂单数据的会员信息
if (data.member_id) {
let res = await getMemberInfoById(data.member_id);
if (res.code == 0 && res.data) {
this.$store.commit('app/setGlobalMemberInfo', res.data);
} else {
this.$store.commit('app/setGlobalMemberInfo', null);
}
}
//取出挂单数据设置到展示列表
let goodsData = {};
data.order_goods.forEach(item => {
if (item.goods_class == 'money') item.money = item.price;
//item.is_adjust = true;
var key = 'sku_' + item.sku_id;
if (item.goods_class == 4 || item.goods_class == 6) {
var index = 0;
Object.keys(goodsData).forEach(k => {
if (k.indexOf(key) != -1) {
index++;
}
});
key += '_' + index;
}
goodsData[key] = item;
});
this.$store.commit('billing/setPendOrderId', data.order_id);
this.$store.commit('billing/setPendOrderNum', this.pendOrderNum - 1);
this.$store.commit('billing/setGoodsData', goodsData);
this.$store.commit('billing/setOrderData', {
goods_list: [],
remark: data.remark
});
this.$store.commit('billing/setActive', 'SelectGoodsAfter');
this.$refs.pendOrderPop.close();
},
switchStoreAfter() {
this.orderData = {
page: 0,
total: 1,
list: []
};
this.getOrder();
},
setHeight() {
this.$nextTick(() => {
const query = uni.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this)
// #endif
query.selectViewport().scrollOffset(data => {
this.height = (data.scrollHeight - 51 - 67 - 15) / 100 + 'rem';
}).exec();
});
}
}
};

View File

@@ -0,0 +1,269 @@
.container {
height: 100%;
display: flex;
flex-direction: column;
.pend-order-scroll-view-wrap {
height: calc(100% - 0.67rem) !important;
box-sizing: border-box;
padding-top: 0.15rem;
padding-bottom: 0;
}
}
.header {
height: 0.66rem;
line-height: 0.66rem;
text-align: left;
border-bottom: 0.01rem solid #e6e6e6;
color: #303133;
font-size: 0.14rem;
}
.table-list {
.table-item {
border: 0.01rem solid #e6e6e6;
padding: 0.15rem;
position: relative;
margin-bottom: 0.2rem;
.table-header-info {
text {
font-size: 0.16rem;
}
.color {
font-size: 0.18rem;
color: #fe2278;
}
}
.table-header-time {
color: #909399;
font-size: 0.14rem;
margin-top: 0.1rem;
.line {
margin: 0 0.15rem;
}
}
.table-header-btn {
position: absolute;
right: 0.3rem;
top: 0.3rem;
color: $primary-color;
cursor: pointer;
}
.table-content {
margin-top: 0.1rem;
.table-content-item {
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 0.01rem solid #e6e6e6;
padding: 0.1rem 0;
.content-item-left {
display: flex;
align-items: center;
flex: 1;
width: 0;
.content-item-info {
padding-right: 0.15rem;
flex: 1;
width: 0;
view {
font-size: 0.14rem;
}
.content-item-name {
margin-bottom: 0.05rem;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
white-space: normal;
word-break: break-all;
}
}
}
.content-item-img {
width: 0.5rem;
height: 0.5rem;
margin-right: 0.1rem;
image {
width: 100%;
height: 100%;
}
}
.content-item-number {
font-size: 0.16rem;
}
.content-item-price {
font-size: 0.14rem;
width: 40%;
text-align: right;
}
}
}
.remark-info {
padding: 0.1rem;
background-color: var(--primary-color-light-9);
color: $primary-color;
margin-top: 0.1rem;
font-size: 0.12rem;
}
.table-bottom {
margin-top: 0.2rem;
display: flex;
justify-content: end;
button {
width: 1rem;
margin: 0;
margin-right: 0.1rem;
}
}
}
}
/deep/ .uni-scroll-view {
&::-webkit-scrollbar {
width: 0.06rem;
height: 0.06rem;
background-color: rgba($color: #000000, $alpha: 0);
}
&::-webkit-scrollbar-button {
display: none;
}
&::-webkit-scrollbar-thumb {
border-radius: 0.06rem;
box-shadow: inset 0 0 0.06rem rgba(45, 43, 43, 0.45);
background-color: #ddd;
display: none;
}
&:hover::-webkit-scrollbar-thumb {
display: block;
}
&::-webkit-scrollbar-track {
background-color: transparent;
}
}
.remark-wrap {
width: 6rem;
background-color: #fff;
border-radius: 0.04rem;
box-shadow: 0 0.01rem 0.12rem 0 rgba(0, 0, 0, 0.1);
.header,
.footer {
height: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 0.01rem solid #e6e6e6;
position: relative;
.title {
font-size: 0.16rem;
font-weight: bold;
}
.iconfont {
line-height: 1;
position: absolute;
right: 0.15rem;
font-size: 0.2rem;
cursor: pointer;
}
}
.body {
padding: 0.15rem;
textarea {
border: 0.01rem solid #e6e6e6;
width: 100%;
padding: 0.1rem;
box-sizing: border-box;
font-size: 0.14rem;
}
.placeholder-class {
font-size: 0.14rem;
}
}
.footer {
border-top: 0.01rem solid #e6e6e6;
border-bottom: unset;
button {
width: 1rem;
}
}
}
.empty {
text-align: center;
padding-top: 1.2rem;
image {
width: 2rem;
}
.tips {
color: #999;
margin-top: 0.15rem;
}
}
// pop弹框
.pop-box {
background: #ffffff;
width: 8rem;
height: 7rem;
.pop-header {
padding: 0 0.15rem 0 0.2rem;
height: 0.5rem;
line-height: 0.5rem;
border-bottom: 0.01rem solid #f0f0f0;
font-size: 0.14rem;
color: #333;
overflow: hidden;
border-radius: 0.02rem 0.2rem 0 0;
box-sizing: border-box;
display: flex;
justify-content: space-between;
.pop-header-text {}
.pop-header-close {
cursor: pointer;
text {
font-size: 0.18rem;
}
}
}
}

View File

@@ -0,0 +1,95 @@
<template>
<view class="container pend-order">
<uni-popup ref="pendOrderPop">
<view class="pop-box">
<view class="pop-header">
<view class="pop-header-text">/取单</view>
<view class="pop-header-close" @click="$refs.pendOrderPop.close()">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<scroll-view scroll-y="true" @scrolltolower="getOrder" class="pend-order-scroll-view-wrap" :style="{height : height}">
<view class="table-list" v-if="orderData.list.length">
<block v-for="(item, index) in orderData.list" :key="index">
<view class="table-item" v-show="item.order_id != orderId">
<view class="table-header">
<view class="table-header-info">
<text>订单总价</text>
<text class="color">{{ item.order_money | moneyFormat }}</text>
</view>
<view class="table-header-time">
<text>挂单时间{{ item.create_time | timeFormat }}</text>
<block v-if="item.member_id">
<text class="line">|</text>
<text>会员{{ item.nickname }}</text>
</block>
</view>
</view>
<view class="table-content">
<view class="table-content-item" v-for="(goods, gindex) in item.order_goods" :key="gindex">
<view class="content-item-left">
<view class="content-item-img">
<image v-if="goods.goods_class == 'money'" src="@/static/goods/goods.png"/>
<image v-else-if="goods.goods_image == '@/static/goods/goods.png'" src="@/static/goods/goods.png"/>
<image v-else :src="$util.img(goods.goods_image, { size: 'small' })" @error="goods.goods_image = '@/static/goods/goods.png'"/>
</view>
<view class="content-item-info">
<view class="content-item-name" v-if="goods.goods_class == 'money'">无码商品</view>
<view class="content-item-name" v-else>
<text>{{ goods.goods_name }}</text>
<text>{{ goods.spec_name }}</text>
</view>
<view>{{ goods.price | moneyFormat }}</view>
</view>
</view>
<view class="content-item-number">x {{ goods.num }}</view>
<view class="content-item-price">{{ (goods.num * goods.price) | moneyFormat }}</view>
</view>
</view>
<view class="remark-info" v-if="item.remark">备注{{ item.remark }}</view>
<view class="table-bottom">
<button class="default-btn btn-left" @click="deleteOrder(item.order_id)">删除</button>
<button class="default-btn btn-left" @click="remarkSetting(item, index)">备注</button>
<button class="primary-btn btn-right" @click="takeOrder(item)">取单</button>
</view>
</view>
</block>
</view>
<view class="empty" v-if="!orderData.list.length || (orderData.list.length == 1 && orderId)">
<image src="@/static/goods/goods_empty.png" mode="widthFix"/>
<view class="tips">暂无挂单记录</view>
</view>
</scroll-view>
</view>
</uni-popup>
<uni-popup ref="remarkPopup" type="center">
<view class="remark-wrap">
<view class="header">
<text class="title">备注</text>
<text class="iconfont iconguanbi1" @click="$refs.remarkPopup.close()"></text>
</view>
<view class="body">
<textarea v-model="remark" placeholder="填写备注信息" placeholder-class="placeholder-class" />
</view>
<view class="footer">
<button type="default" class="primary-btn" @click="remarkConfirm">确认</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import index from './index.js';
export default {
mixins: [index]
};
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

View File

@@ -0,0 +1,127 @@
import {getAddonIsExist,getPromotionQrcode} from '@/api/promotion.js';
export default {
name: 'nsPromotionPopup',
props: {
pageName: {
type: String,
default: 'COUPON_DETAIL'
},
},
data() {
return {
qrParams:{
page_name:'',
option:'',
app_type:'h5'
},
APPType:'h5',
appTypeArray: [{
text: 'H5',
value: 'h5'
}],
qrData:{}
}
},
mounted() {
this.qrParams.page_name = this.pageName
this.getAddonIsExistFn()
},
methods:{
getAddonIsExistFn(){
getAddonIsExist().then(res=>{
if(res.data.weapp){
this.appTypeArray.push({text:'微信小程序',value:'weapp'})
}
if(res.data.aliapp){
this.appTypeArray.push({text:'支付宝小程序',value:'aliapp'})
}
})
},
getPromotionQrcodeFn(){
getPromotionQrcode(this.qrParams).then(res=>{
this.qrData = Object.assign(this.qrData,res.data)
this.$forceUpdate();
})
},
open(option){
this.qrParams.option = JSON.stringify(option)
this.$refs.promotionPop.open()
this.qrData={}
this.appTypeArray.forEach((el)=>{
this.qrParams.app_type = el.value
this.getPromotionQrcodeFn()
})
},
//复制链接
copyTextToClipboard(text) {
uni.setClipboardData({
data: text,
success: function () {
// 可以添加用户友好的提示例如使用uni.showToast提示复制成功
uni.showToast({
title: '复制成功',
icon: 'success',
duration: 2000
});
},
fail: function () {
console.log('复制失败');
// 可以添加错误处理或用户友好的提示
}
});
},
//下载二维码
download(url){
var oA = document.createElement("a");
oA.innerHTML = '123'
oA.download = ''; // 设置下载的文件名,默认是'下载'
oA.target = "_blank"
oA.href = url; //临时路径再保存到本地
document.body.appendChild(oA);
oA.click();
oA.remove(); // 下载之后把创建的元素删除
}
// download(url){
// //下载文档
// uni.downloadFile({
// url: url,//下载地址接口返回
// success: (data) => {
// if (data.statusCode === 200) {
// //文件保存到本地
// uni.saveFile({
// tempFilePath: data.tempFilePath, //临时路径
// success: function(res) {
// uni.showToast({
// icon: 'none',
// mask: true,
// title: '文件已保存:' + res.savedFilePath, //保存路径
// duration: 3000,
// });
// setTimeout(() => {
// //打开文档查看
// uni.openDocument({
// filePath: res.savedFilePath,
// success: function(res) {
// // console.log('打开文档成功');
// }
// });
// }, 3000)
// }
// });
// }
// },
// fail: (err) => {
// console.log(err);
// uni.showToast({
// icon: 'none',
// mask: true,
// title: '失败请重新下载',
// });
// },
// });
// }
}
}

View File

@@ -0,0 +1,76 @@
.promotion-pop{
width: 7rem;
background-color: #fff;
border-radius: 0.06rem;
.header{
padding: 0.15rem 0.2rem;
font-size: 0.14rem;
border-bottom: 0.01rem solid #e6e6e6;
}
.body{
width: 100%;
padding: 0.2rem 0.3rem;
box-sizing: border-box;
.alter{
height: 0.48rem;
line-height: 0.48rem;
font-size: 0.14rem;
padding: 0 0.2rem;
color: #666;
background-color: var(--primary-color-light-9);
margin-bottom: 0.2rem;
}
.content{
.qrCode{
width: 2rem;
height: 2rem;
background-color: #f8f8f8;
color: #333;
font-size: 0.14rem;
image{
width:1.6rem;
height:1.6rem;
}
}
.right{
margin-left: 0.2rem;
.form-item{
margin-bottom: 0.1rem;
}
.link {
.form-inline{
margin-top: 0.1rem;
}
}
input{
width: 2rem;
height: 0.3rem;
border: 0.01rem solid #e6e6e6;
padding: 0 0.12rem;
font-size: 0.14rem;
border-radius: 0.02rem;
box-sizing: border-box;
}
.btn{
background-color: var(--primary-color);
color: #fff;
margin-left: .1rem;
font-size: 0.14rem;
height: 0.3rem;
line-height: 0.3rem;
&::after{
border: 0;
}
}
.download{
color: var(--primary-color);
cursor: pointer;
}
}
}
}
}

View File

@@ -0,0 +1,60 @@
<template>
<unipopup ref="promotionPop" type="center">
<view class="promotion-pop">
<view class="header flex justify-between">
<view class="title">推广</view>
<view class="pop-header-close" @click="$refs.promotionPop.close()">
<text class="iconguanbi1 iconfont"></text>
</view>
</view>
<view class="body">
<view class="alter">活动可分享至多个渠道推广增加曝光率提升分享打开率</view>
<view class="flex content">
<view class="qrCode flex items-center justify-center">
<image v-if="qrData[APPType]&&qrData[APPType].path" :src="$util.img(qrData[APPType].path)"/>
<text v-else>小程序配置错误</text>
</view>
<view class="flex-1 right">
<view class="form-box">
<view class="form-content">
<view class="form-item flex">
<view class="form-label">充值方式</view>
<view class="form-inline">
<uni-data-checkbox v-model="APPType" :localdata="appTypeArray" />
</view>
</view>
<view class="form-item link" v-if="APPType == 'h5'&&qrData[APPType]&&qrData[APPType].url">
<view class="form-label">
推广链接
</view>
<view class="form-inline flex items-center">
<input type="text" disabled v-model="qrData[APPType].url" @keydown.enter="search('enter')" />
<button type="default" class="btn" @click="copyTextToClipboard(qrData[APPType].url)">复制</button>
</view>
</view>
<view class="form-item" v-if="qrData[APPType]&&qrData[APPType].path">
<text class="download" @click="download($util.img(qrData[APPType].path))">下载二维码</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</unipopup>
</template>
<script>
import unipopup from '@/components/uni-popup/uni-popup.vue';
import index from './index.js';
export default {
components: {
unipopup,
},
mixins: [index]
};
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,384 @@
<template>
<view>
<uni-popup ref="memberInquirePopup" type="center" @change="popupChange" :mask-click="false">
<view class="popup-inquire-wrap">
<view class="popup-header">
<text class="title">称重商品同步到电子秤</text>
<text class="iconfont iconguanbi1" @click="$refs.memberInquirePopup.close()"></text>
</view>
<view class="popup-content">
<view v-show="step == 1">
<view class="content-title">选择商品</view>
<uniDataTable url="/weighgoods/storeapi/goods/skuall" :cols="cols" :classType="true" @checkBox="checkBox" ref="goodsListTable" />
</view>
<view v-show="step == 2">
<view class="content-title">选择需同步的电子秤</view>
<uniDataTable url="/scale/storeapi/scale/page" :pagesize="0" :cols="scaleCols" :classType="true" ref="scaleListTable" @tableData="onloadScale" />
</view>
<view v-show="step == 3">
<view class="content-title">同步商品到电子秤</view>
<uniDataTable :cols="syncTaskCols" :classType="true" ref="syncTaskTable" :data="syncTask" />
</view>
</view>
<view class="popup-footer" v-show="step == 1">
<button type="default" class="default-btn" @click="next">下一步</button>
</view>
<view class="popup-footer" v-show="step == 2">
<button type="default" class="default-btn" @click="step = 1">上一步</button>
<button type="primary" class="primary-btn" @click="syncGoods">同步</button>
</view>
<view class="popup-footer" v-show="step == 3">
<button type="default" class="primary-btn" :loading="synching" v-if="synching">同步中</button>
<button type="default" class="primary-btn" :loading="synching" @click="$refs.memberInquirePopup.close()" v-else>完成</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import uniDataTable from '@/components/uni-data-table/uni-data-table.vue';
import { getScaleList } from '@/api/scale.js';
import {
createCommand
} from './command.js'
var self;
export default {
components: {
uniDataTable
},
data() {
return {
step: 1,
cols: [{
width: 6,
align: 'center',
checkbox: true
},
{
field: 'account_data',
width: 50,
title: '商品信息',
align: 'left',
templet: data => {
let img = this.$util.img(data.sku_image);
let html = `
<view class="goods-content">
<image class="goods-img" src="${img}" mode="aspectFit"/>
<text class="goods-name multi-hidden">${data.sku_name}</text>
</view>
`;
return html;
}
},
{
width: 10,
title: '价格',
align: 'center',
templet: function(data) {
return '¥' + data.price;
}
},
{
field: 'plu',
width: 10,
title: 'PLU码',
align: 'center'
},
{
width: 10,
title: '计价方式',
align: 'center',
templet: function(data) {
if (data.pricing_type == 'num') {
return '计数';
} else {
return '计重';
}
}
},
{
width: 10,
title: '状态',
align: 'center',
templet: function(data) {
var str = '';
if (data.store_status == 1) {
str = '销售中';
} else if (data.store_status == 0) {
str = '仓库中';
}
return str;
}
}
],
scaleCols: [{
width: 6,
align: 'center',
checkbox: true,
disabled: (data) => {
return !data.connect_status
}
},
{
width: 24,
title: '设备名称',
align: 'left',
field: 'name',
},
{
width: 20,
title: '设备品牌',
align: 'center',
field: 'brand_name',
},
{
width: 20,
title: '设备型号',
align: 'center',
field: 'model_name',
},
{
width: 20,
title: '状态',
align: 'center',
templet: (data) => {
return data.connect_status ? '已连接' : '未连接'
}
}
],
syncTaskCols: [{
width: 70,
title: '设备名称',
align: 'left',
templet: (data) => {
return data.name
}
},
{
width: 30,
title: '同步状态',
align: 'left',
templet: (data) => {
var str = '';
switch (data.syncStatus) {
case '1':
str = `<view>同步成功</view>`;
break;
case '0':
str = `<view>同步失败</view><view style="color: red;display:block;white-space: normal;">失败原因:${data.msg}</view>`;
break;
default:
str = `<view>同步中</view>`;
break;
}
return str;
}
}
],
syncTask: {},
synching: false
};
},
created() {
this.getScaleListFn();
self = this;
},
methods: {
popupChange(e) {
if (!e.show) {
this.syncTask = {};
this.synching = false;
this.step = 1
}
},
open() {
this.$refs.memberInquirePopup.open();
},
selectScale(e) {
this.scale = this.scaleList[e].name;
this.scaleId = this.scaleList[e].scale_id
},
checkBox(e) {
this.goodsList = e;
},
getScaleListFn() {
if (!this.addon.includes('scale')) {
return;
}
getScaleList({
page: 1,
page_size: 100
}).then(res=>{
if (res.data.list.length > 0) this.scaleList = res.data.list;
});
},
next() {
const selected = this.$refs.goodsListTable.selected;
if (!selected.length) {
this.$util.showToast({
'title': '请选择要同步的商品'
});
return
}
this.step += 1;
},
onloadScale(list) {
if (typeof window.POS_DATA_CALLBACK == 'function') delete window.POS_DATA_CALLBACK;
/**
* 商品同步数据回调
* @param {Object} text
*/
window.POS_DATA_CALLBACK = function(text) {
let data = text.split(':');
let index = parseInt(data[0]);
switch (data[1]) {
case 'SyncGoodsPlu':
self.$set(self.syncTask[index], 'syncStatus', data[2]);
self.$set(self.syncTask[index], 'msg', data[4]);
if (index == self.syncTask.length - 1) {
self.synching = false
}
break;
case 'PingWeigher':
self.$set(self.$refs.scaleListTable.list[index], 'connect_status', parseInt(data[3]));
break;
}
};
let weigher = list.map(item => {
item.config = JSON.parse(item.config);
return item;
});
try {
this.$pos.send('PingWeigher', JSON.stringify({
weigher
}));
} catch (e) {}
},
async syncGoods() {
const selected = this.$refs.scaleListTable.selected;
if (!selected.length) {
this.$util.showToast({
'title': '请选择要同步的设备'
});
return;
}
if (this.synching) return;
this.synching = true;
this.syncTask = this.$refs.scaleListTable.selected
this.step += 1;
setTimeout(() => {
this.createSyncData()
}, 100)
},
createSyncData() {
let task = {};
task.weigher = this.$refs.scaleListTable.selected.map(scale => {
scale.config = typeof scale.config == 'string' ? JSON.parse(scale.config) : scale.config;
scale.goodsList = this.$refs.goodsListTable.selected.map(sku => {
return {
sku_no: sku.sku_no,
plu: sku.plu,
price: sku.price,
sku_name: sku.sku_name,
pricing_type: sku.pricing_type,
command: ''
}
});
return scale;
});
this.syncTask = task.weigher;
try {
console.log(JSON.stringify(task));
this.$pos.send('SyncGoodsPlu', JSON.stringify(task));
} catch (e) {
this.synching = false
}
}
}
};
</script>
<style lang="scss" scoped>
.popup-inquire-wrap {
overflow: hidden;
width: 9.55rem;
height: 5.37rem;
background-color: #fff;
border-radius: 0.05rem;
display: flex;
flex-direction: column;
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 0.15rem;
height: 0.45rem;
line-height: 0.45rem;
border-bottom: 0.01rem solid #e8eaec;
.iconfont {
font-size: $uni-font-size-lg;
}
}
.popup-content {
flex: 1;
height: 0;
overflow-y: auto;
padding: 0.1rem;
/deep/ .content {}
.content-title {
margin-bottom: .1rem;
}
}
.popup-footer {
padding: 0.1rem;
display: flex;
justify-content: end;
button {
width: 1rem;
margin: 0 0 0 .15rem;
}
}
}
/deep/.goods-content {
display: flex;
.goods-img {
margin-right: 0.1rem;
width: 0.5rem;
height: 0.5rem;
}
.goods-name {
white-space: pre-wrap;
align-self: baseline;
}
}
/deep/.uni-select-lay-select {
width: 2rem !important;
height: 0.42rem !important;
}
</style>

View File

@@ -0,0 +1,242 @@
import {getMemberList,getMemberInfoById, getMemberLevelList, addMember, searchMemberByMobile} from '@/api/member.js';
import {mapGetters} from 'vuex';
export default {
data() {
return {
searchText: '',
page: 1,
memberList: [],
memberId: '',
memberData: {
sex: 0,
mobile: '',
nickname: '',
birthday: '',
member_level: '',
member_level_name: ''
},
memberLevelList: [], // 会员等级
sex: [{
text: '未知',
value: 0
}, {
text: '男',
value: 1
}, {
text: '女',
value: 2
}],
memberType: 'login',
flag: false,
inputFocus: false,
isPhone: false,
searchFinish: false // 搜索是否完成
};
},
created() {
this.getMemberLevel();
},
computed: {
...mapGetters(['memberSearchWayConfig'])
},
watch: {
memberSearchWayConfig: {
immediate: true,
handler(newVal, oldVal) {
if(newVal) {
if(newVal.way == 'list'){
this.getMemberListFn();
}
}
}
}
},
methods: {
searchMemberInputBlur(){
this.inputFocus = false;
//强制聚焦处理
if(this.memberType = 'login'){
this.$nextTick(() => {
this.inputFocus = true;
});
}
},
open(callback) {
this.memberId = this.globalMemberInfo ? this.globalMemberInfo.member_id + '' : '';
this.$refs.memberPopup.open('', callback);
this.inputFocus = true;
this.searchFinish = false;
},
// 查询会员列表
searchMemberByMobileFn() {
setTimeout(() => {
if (!this.searchText) return false;
searchMemberByMobile({
mobile: this.searchText
}).then((res) => {
if (res.code >= 0) {
this.$store.commit('app/setGlobalMemberInfo', res.data);
this.initData();
this.$refs.memberPopup.close();
} else {
if (res.data > 1) {
this.$util.showToast({
title: res.message
});
return false;
}
var regex = /^1[3-9]\d{9}$/;
if (res.data == 0 && regex.test(this.searchText)) {
this.isPhone = true;
this.$refs.emptyPopup.open();
return false;
}
if (res.data == 0) {
this.isPhone = false;
this.$refs.emptyPopup.open();
return false;
}
}
});
}, 200)
},
getMemberInfo(memberId, callback) {
this.memberId = memberId;
getMemberInfoById(memberId).then(res => {
if (res.code == 0 && res.data) {
this.$store.commit('app/setGlobalMemberInfo', res.data);
if (callback) callback();
this.initData();
this.$refs.memberPopup.close();
} else {
this.$util.showToast({
title: '未获取到会员信息'
});
}
})
},
/******************************** 录入会员 ********************************/
getMemberLevel() {
this.memberLevelList = [];
getMemberLevelList().then(res => {
if (res.code == 0 && res.data) {
for (let i in res.data) {
this.memberLevelList.push({
label: res.data[i]['level_name'],
value: res.data[i]['level_id'].toString(),
disabled: false
});
}
}
});
},
// 选择会员等级
selectMemberLevel(index, item) {
if (index >= 0) {
this.memberData.member_level = item.value;
this.memberData.member_level_name = item.label;
} else {
this.memberData.member_level = '';
this.memberData.member_level_name = '';
}
this.$forceUpdate();
},
// 选择时间
changeTime(e) {
this.memberData.birthday = e;
},
verify() {
if (!this.memberData.mobile) {
this.$util.showToast({
title: '请输入会员手机号'
});
return false;
}
if (!this.$util.verifyMobile(this.memberData.mobile)) {
this.$util.showToast({
title: '请输入正确的手机号码'
});
return false;
}
return true;
},
// 确定录入
addMemberFn() {
if (this.verify()) {
if (this.flag) return;
this.flag = true;
addMember(this.memberData).then(res => {
if (res.code == 0 && res.data) {
this.memberType = 'login';
this.getMemberInfo(res.data)
} else {
this.$util.showToast({
title: res.message
});
}
this.flag = false;
})
}
},
closedFn() {
this.memberType = "login";
this.$refs.memberPopup.close();
},
memberEmptyRegister() {
this.memberType = "register";
this.memberData.mobile = this.searchText;
this.$refs.emptyPopup.close();
},
initData() {
this.searchText = '';
this.memberData.sex = 0;
this.memberData.mobile = '';
this.memberData.nickname = '';
this.memberData.birthday = '';
this.memberData.member_level = '';
this.memberData.member_level_name = '';
},
stayTuned() {
this.$util.showToast({
title: '敬请期待'
});
},
getMemberListFn(isSearch){
getMemberList({
page: this.page,
page_size: 12,
search_text: this.searchText
}).then((res)=>{
if (res.code >= 0) {
if (this.page == 1) this.memberList = [];
this.memberList = this.memberList.concat(res.data.list);
this.memberList.forEach((item) => {
if (item.mobile) {
if (this.userInfo && this.userInfo.is_admin == 0) {
// 非管理员,不能查看会员手机号
item.mobile = item.mobile.substring(0, 4 - 1) + '****' + item.mobile.substring(6 + 1);
}
} else {
item.mobile = '--';
}
});
if (isSearch) {
// 默认选中第一个搜索结果
this.memberId = 0;
if (this.memberList.length) {
this.memberId = this.memberList[0].member_id;
}
}
this.searchFinish = true;
if (res.data.page_count >= this.page) this.page++;
}
})
},
searchMemberByList(){
this.page = 1;
this.getMemberListFn(Boolean(this.searchText));
}
}
};

View File

@@ -0,0 +1,333 @@
.member-inquire-wrap {
overflow: hidden;
background-color: #fff;
border-radius: 0.05rem;
.member-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 0.15rem;
height: 0.45rem;
line-height: 0.45rem;
border-bottom: 0.01rem solid #e8eaec;
.iconfont {
font-size: $uni-font-size-lg;
}
}
&.exact{
width: 4rem;
.member-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0.3rem 0.3rem;
.member-img{
width: 0.75rem;
height: 0.75rem;
}
.member-input{
margin-top: .25rem;
width: 100%;
height: .4rem;
line-height: .4rem;
// border-radius: 0.02rem;
padding: 0 0.1rem;
border: 0.01rem solid $primary-color;
box-sizing: border-box;
text-align: center;
}
button{
// border-radius: 0.05rem;
height: .4rem;
line-height: .4rem;
margin-top: 0.15rem;
width: 100%;
}
.function-list{
margin-top: .25rem;
padding-top: .15rem;
border-top: 0.01rem dashed #ccc;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.item-wrap{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
.item-icon{
font-size: .25rem;
color: #333;
margin-bottom: 0.05rem;
}
}
}
}
}
&.list{
width: 9.55rem;
height: 5.37rem;
.member-content {
padding: 0.1rem;
.search-warp {
margin-left: 0.1rem;
.search-input {
display: flex;
align-items: center;
margin-bottom: 0.05rem;
input {
flex: 1;
padding-left: 0.1rem;
width: 2.5rem;
height: 0.4rem;
line-height: 0.4rem;
border: 0.01rem solid #dcdee2;
border-right: none;
}
button {
width: 2rem;
height: 0.42rem;
line-height: 0.42rem;
color: #fff;
font-size: $uni-font-size-base;
margin: 0;
&:last-of-type {
margin-left: 0.15rem;
margin-right: 0.1rem;
}
}
}
}
/deep/ .uni-scroll-view-content {
display: flex;
flex-wrap: wrap;
align-content: baseline;
height: 430px;
}
.member-list {
.member-item {
display: flex;
padding: 0.13rem 0.15rem;
margin: 0.1rem;
width: 2.9rem;
height: 1rem;
background-color: #f5f5f5;
box-sizing: border-box;
border-radius: 0.05rem;
&.active {
background-color: $primary-color;
.name,
.phone,
.other>view {
color: #fff;
}
}
&:nth-child(3n + 3) {
margin-right: 0;
}
image {
width: 0.45rem;
height: 0.45rem;
border-radius: 50%;
margin-right: 0.1rem;
flex-shrink: 0;
}
.item-content {
display: flex;
flex-direction: column;
justify-content: space-between;
flex: 1;
width: calc( 100% - 0.55rem );
.name{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.name {
text:nth-child(1) {
font-size: $uni-font-size-lg;
font-weight: bold;
}
}
.phone {
font-size: $uni-font-size-sm;
color: #666;
}
.other {
display: flex;
justify-content: space-between;
font-size: $uni-font-size-sm;
view {
font-size: $uni-font-size-sm;
color: #666;
}
}
}
}
}
}
}
// 录入会员
.member-entering-wrap {
width: 3.8rem;
background-color: #fff;
border-radius: 0.05rem;
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 0.15rem;
height: 0.45rem;
line-height: 0.45rem;
border-bottom: 0.01rem solid #e8eaec;
.iconfont {
font-size: $uni-font-size-lg;
}
}
.form-content {
display: flex;
flex-direction: column;
align-items: center;
padding: 0.2rem;
.form-item {
margin-bottom: 0.1rem;
display: flex;
.form-label {
width: 0.9rem;
text-align: right;
padding-right: 0.1rem;
box-sizing: border-box;
height: 0.32rem;
line-height: 0.32rem;
.required {
color: red;
margin-right: 0.03rem;
}
}
.form-inline {
width: 2.5rem;
line-height: 0.32rem;
box-sizing: border-box;
.form-input {
border-width: 0.01rem;
border-style: solid;
background-color: #fff;
color: rgba(0, 0, 0, 0.85);
border-radius: 0.02rem;
padding-left: 0.1rem;
height: 0.32rem;
line-height: 0.32rem;
font-size: 0.14rem;
border-color: #e6e6e6;
}
button {
width: calc(50% - 0.05rem);
display: inline-block;
margin-right: 0.1rem;
&:nth-child(2) {
margin-right: 0;
}
}
}
}
.btn-wrap {
width: 100%;
box-sizing: border-box;
padding: 0.1rem 0;
.primary-btn {
height: 0.4rem;
line-height: 0.4rem;
}
}
}
}
.empty {
text-align: center;
padding-top: 0.8rem;
width: 100%;
image {
width: 2rem;
}
.tips {
color: #999;
margin-top: 0.15rem;
}
}
.member-empty{
overflow: hidden;
background-color: #fff;
border-radius: 0.02rem;
width: 3rem;
.head{
display: flex;
align-items: center;
padding-left: .2rem;
height: .42rem;
background-color: #f8f8f8;
border-bottom: .01rem solid #eee;
}
.content{
padding: .06rem .2rem 0;
height: .6rem;
line-height: .6rem;
}
.btn-wrap{
display: flex;
justify-content: flex-end;
padding-right: .2rem;
padding-bottom: .2rem;
button{
margin: 0;
margin-left: .1rem;
border-radius: .02rem;
}
.close-btn{
font-size: 0.14rem;
}
}
}

View File

@@ -0,0 +1,154 @@
<template>
<view>
<uni-popup ref="memberPopup" type="center" @maskClick="closedFn">
<view class="member-inquire-wrap" :class="{ 'exact' : memberSearchWayConfig.way == 'exact','list' : memberSearchWayConfig.way == 'list' }" v-if="memberType == 'login'">
<view class="member-header">
<text class="title">{{ memberSearchWayConfig.way == 'exact' ? '会员查询' : '会员列表' }}</text>
<text class="iconfont iconguanbi1" @click="closedFn"></text>
</view>
<view class="member-content" v-if="memberSearchWayConfig.way == 'exact'">
<image class="member-img" mode="aspectFill" src="@/static/member/head.png" />
<input type="number" class="member-input" focus placeholder="请输入手机号或手机号后四位" placeholder-style="font-size:0.14rem" v-model="searchText" @confirm="searchMemberByMobileFn()" :focus="inputFocus" @focus="inputFocus = true" @blur="searchMemberInputBlur" />
<button class="switch primary-btn" @click="searchMemberByMobileFn()">查询</button>
<view class="function-list">
<view class="item-wrap" @click="stayTuned">
<text class="item-icon iconfont iconmenpos"></text>
<text>刷卡登录</text>
</view>
<view class="item-wrap" @click="stayTuned">
<!-- <image class="item-img" mode="aspectFill" src="@/static/member/head.png" /> -->
<text class="item-icon iconfont iconsaomiaoerweima"></text>
<text>扫码登录</text>
</view>
<view class="item-wrap" @click="stayTuned">
<text class="item-icon iconfont iconhuaxiangfenxi"></text>
<text>人脸登录</text>
</view>
<view class="item-wrap" @click="memberType = 'register'">
<text class="item-icon iconfont iconhuiyuanzhucedengluguanli"></text>
<text>会员注册</text>
</view>
</view>
</view>
<view class="member-content" v-if="memberSearchWayConfig.way == 'list'">
<view class="search-warp">
<view class="search-input">
<input focus placeholder="可查询会员账号、手机号、昵称" placeholder-style="font-size:0.14rem" v-model="searchText" @confirm="searchMemberByList()" :focus="inputFocus" @focus="inputFocus = true" @blur="searchMemberInputBlur" />
<button class="switch primary-btn" @click="searchMemberByList()">查询 [Enter]</button>
<button class="default-btn" plain="true" @click="memberType = 'register'">添加会员</button>
</view>
</view>
<scroll-view @scrolltolower="getMemberListFn()" scroll-y="true" class="member-list">
<view :class="['member-item', { active: item.member_id == memberId }]" v-for="(item, index) in memberList" :key="index" @click="getMemberInfo(item.member_id)">
<image class="item-img" mode="aspectFill" v-if="item.headimg" :src="$util.img(item.headimg)" @error="item.headimg = defaultImg.head"/>
<image class="item-img" mode="aspectFill" v-else :src="$util.img(defaultImg.head)"/>
<view class="item-content">
<view class="name">
<text :title="item.nickname">{{ item.nickname }}</text>
</view>
<view class="phone">手机号{{ item.mobile }}</view>
<view class="other">
<view>余额{{ parseFloat(parseFloat(item.balance) + parseFloat(item.balance_money)).toFixed(2) }}</view>
</view>
</view>
</view>
<view v-show="memberList.length == 0" class="empty">
<image :src="$util.img('public/uniapp/cashier/member-empty.png')" mode="widthFix"/>
<view class="tips">暂无会员</view>
</view>
</scroll-view>
</view>
</view>
<view class="member-entering-wrap" v-if="memberType == 'register'">
<view class="header">
<text class="iconfont iconqianhou1" @click="memberType = 'login'"></text>
<text class="title">录入会员</text>
<text class="iconfont iconguanbi1" @click="closedFn"></text>
</view>
<view class="form-content">
<view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
手机号
</view>
<view class="form-inline">
<input type="number" class="form-input" v-model="memberData.mobile" placeholder="请输入会员手机号" />
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
会员名称
</view>
<view class="form-inline">
<input type="text" class="form-input" v-model="memberData.nickname" placeholder="请输入会员昵称" />
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
性别
</view>
<view class="form-inline">
<uni-data-checkbox v-model="memberData.sex" :localdata="sex"/>
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required"></text>
生日
</view>
<view class="form-inline">
<uni-datetime-picker v-model="memberData.birthday" type="date" :clearIcon="false" @change="changeTime" />
</view>
</view>
<view class="form-item" v-if="memberLevelList.length">
<view class="form-label">
<text class="required"></text>
会员等级
</view>
<view class="form-inline">
<select-lay :zindex="10" :value="memberData.member_level" name="names" placeholder="请选择会员等级" :options="memberLevelList" @selectitem="selectMemberLevel"/>
</view>
</view>
</view>
<view class="btn-wrap">
<button type="primary" class="primary-btn" @click="addMemberFn">确定录入</button>
</view>
</view>
</view>
</uni-popup>
<uni-popup ref="emptyPopup" type="center">
<view class="member-empty">
<view class="head">提示</view>
<view class="content">未找到顾客{{searchText}}</view>
<view class="btn-wrap">
<button class="close-btn" @click="$refs.emptyPopup.close()">关闭</button>
<button class="primary-btn" v-if="isPhone" @click="memberEmptyRegister()">注册</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import index from './index.js';
export default {
mixins: [index]
};
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

View File

@@ -0,0 +1,146 @@
<template>
<view>
<uni-popup ref="updatePopup" type="center" :maskClick="false" v-if="versionInfo">
<view class="update-wrap">
<view class="head"><image src="@/static/cashier/update_header.png" /></view>
<view class="body">
<view class="version-no">版本号{{ versionInfo.version }}</view>
<view class="title">更新内容</view>
<view class="desc common-scrollbar">{{ versionInfo.update_desc }}</view>
<button type="default" class="primary-btn" @click="update">立即更新</button>
<view class="giveup-update" @click="giveupUpdate" v-if="!versionInfo.is_force_upgrade">以后再说</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
/**
* app版本更新
*/
import {checkUpdate} from '@/api/config.js'
export default {
data() {
return {
versionInfo: null
};
},
created() {
// wifi模式下才检测升级
if (plus.networkinfo.getCurrentType() == plus.networkinfo.CONNECTION_WIFI) {
this.checkUpdateFn();
}
},
methods: {
/**
* 检测是否有新版本
*/
checkUpdateFn() {
checkUpdate({
app_key: this.$config.app.app_key,
version: this.$config.app.version_no,
platform: uni.getSystemInfoSync().platform
}).then(res=>{
if (res.code == 0 && res.data) {
this.versionInfo = res.data;
if (!uni.getStorageSync('version_' + this.versionInfo.version_no)) {
this.$refs.updatePopup.open();
}
}
})
},
/**
* 确认更新
*/
update() {
let systemInfo = uni.getSystemInfoSync();
if (systemInfo.platform == 'android') {
uni.showLoading({});
uni.downloadFile({
url: this.$util.img(this.versionInfo.package_path),
success: data => {
uni.hideLoading();
if (data.statusCode === 200) {
plus.runtime.install(
data.tempFilePath,
{
force: false
},
function() {
plus.runtime.restart();
}
);
}
},
fail: res => {
this.$util.showToast({ title: '安装包下载失败' });
uni.hideLoading();
}
});
} else if (systemInfo.platform == 'ios') {
plus.runtime.launchApplication({ action: this.versionInfo.package_path }, e => {
this.$util.showToast({ title: e.message });
this.$refs.updatePopup.close();
});
}
},
/**
* 放弃本次更新
*/
giveupUpdate() {
uni.setStorageSync('version_' + this.versionInfo.version_no, 1);
this.$refs.updatePopup.close();
}
}
};
</script>
<style lang="scss" scoped>
.update-wrap {
width: 3rem;
.head {
height: 0.98rem;
image {
width: 3rem;
height: 0.98rem;
}
}
.body {
padding: 0.2rem 0.3rem;
background: #fff;
.version-no {
margin-bottom: 0.15rem;
}
.desc {
max-height: 1rem;
}
.title {
font-size: 0.16rem;
font-weight: 700;
margin-bottom: 0.15rem;
}
.primary-btn {
margin-top: 0.15rem;
}
.giveup-update {
margin-top: 0.15rem;
text-align: center;
line-height: 1;
}
}
}
/deep/ .uni-popup {
z-index: 1010;
}
</style>

View File

@@ -0,0 +1,220 @@
<template>
<view class="pick-regions">
<picker mode="multiSelector" :value="multiIndex" :range="multiArray" @change="handleValueChange" @columnchange="handleColumnChange">
<slot></slot>
</picker>
</view>
</template>
<script>
import {getAreaList} from '@/api/address.js'
export default {
props: {
defaultRegions: {
type: Array
},
selectArr: {
type: String
}
},
data() {
return {
pickerValueArray: [],
cityArr: [],
districtArr: [],
multiIndex: [0, 0, 0],
isInitMultiArray: false,
// 是否加载完默认地区
isLoadDefaultAreas: false
};
},
watch: {
defaultRegions: {
handler(arr, oldArr = []) {
// 避免传的是字面量的时候重复触发
if (arr.length != this.selectArr || arr.join('') === oldArr.join('')) return;
this.handleDefaultRegions();
},
immediate: true
}
},
computed: {
multiArray() {
if (!this.isLoadDefaultAreas) return;
var arr = this.pickedArr.map(arr => arr.map(item => item.label));
return arr;
},
pickedArr() {
// 进行初始化
if (this.isInitMultiArray) {
if (this.selectArr == '2') {
return [this.pickerValueArray[0], this.pickerValueArray[1]];
} else {
return [this.pickerValueArray[0], this.pickerValueArray[1], this.pickerValueArray[2]];
}
}
if (this.selectArr == '2') {
return [this.pickerValueArray[0], this.cityArr];
} else {
return [this.pickerValueArray[0], this.cityArr, this.districtArr];
}
}
},
created() {
this.getDefaultAreas(0, { level: 0 });
},
methods: {
async handleColumnChange(e) {
this.isInitMultiArray = false;
let col = e.detail.column;
let row = e.detail.value;
this.multiIndex[col] = row;
switch (col) {
case 0:
//选择省,加载市、区县
this.cityArr = await this.getAreasAsync(this.pickerValueArray[0][this.multiIndex[col]].value);
this.districtArr = await this.getAreasAsync(this.cityArr[0].value);
break;
case 1:
//选择市,加载区县
this.districtArr = await this.getAreasAsync(this.cityArr[this.multiIndex[col]].value);
break;
case 2:
if (!this.cityArr.length) this.cityArr = await this.getAreasAsync(this.pickerValueArray[0][0].value)
if (!this.districtArr.length) this.districtArr = await this.getAreasAsync(this.cityArr[0].value);
break;
}
},
handleValueChange(e) {
// 结构赋值
let [index0, index1, index2] = e.detail.value;
let [arr0, arr1, arr2] = this.pickedArr;
let address = '';
if (this.selectArr == '2') {
address = [arr0[index0], arr1[index1]];
} else {
address = [arr0[index0], arr1[index1], arr2[index2]];
}
this.$emit('getRegions', address);
},
handleDefaultRegions() {
var time = setInterval(() => {
if (!this.isLoadDefaultAreas) return;
this.isInitMultiArray = false;
for (let i = 0; i < this.defaultRegions.length; i++) {
for (let j = 0; j < this.pickerValueArray[i].length; j++) {
// 匹配省
if ( (this.defaultRegions[i] == this.pickerValueArray[i][j].value || this.defaultRegions[i] == this.pickerValueArray[i][j].label) && this.pickerValueArray[i][j].level == 1) {
// 设置选中省
this.$set(this.multiIndex, i, j);
// 查询市
this.getAreas(this.pickerValueArray[i][j].value, data => {
this.cityArr = data;
for (let k = 0; k < this.cityArr.length; k++) {
if (this.defaultRegions[1] == this.cityArr[k].value || this.defaultRegions[1] == this.cityArr[k].label) {
// 设置选中市
this.$set(this.multiIndex, 1, k);
// 查询区县
this.getAreas(this.cityArr[k].value, data => {
this.districtArr = data;
// 设置选中区县
for (let u = 0; u < this.districtArr.length; u++) {
if (this.defaultRegions[2] == this.districtArr[u].value || this.defaultRegions[2] == this.districtArr[u].label) {
this.$set(this.multiIndex, 2, u);
this.handleValueChange({
detail: {
value: [j, k, u]
}
});
break;
}
}
});
break;
}
}
});
}
}
}
if (this.isLoadDefaultAreas) clearInterval(time);
}, 100);
},
getDefaultAreas(pid, obj) {
getAreaList({ pid: pid }).then(res=>{
if (res.code == 0) {
var data = [];
var selected = undefined;
res.data.forEach((item, index) => {
if (obj != undefined) {
if (obj.level == 0 && obj.province_id != undefined) {
selected = obj.province_id;
} else if (obj.level == 1 && obj.city_id != undefined) {
selected = obj.city_id;
} else if (obj.level == 2 && obj.district_id != undefined) {
selected = obj.district_id;
}
}
if (selected == undefined && index == 0) {
selected = item.id;
}
data.push({
value: item.id,
label: item.name,
level: item.level
});
});
this.pickerValueArray[obj.level] = data;
if (obj.level + 1 < 3) {
obj.level++;
this.getDefaultAreas(selected, obj);
} else {
this.isInitMultiArray = true;
this.isLoadDefaultAreas = true;
}
}
});
},
// 同步获取地区
async getAreasAsync(pid) {
let res = await getAreaList({ pid: pid });
if (res.code == 0) {
var data = [];
res.data.forEach((item, index) => {
data.push({
value: item.id,
label: item.name,
level: item.level
});
});
return data;
}
},
// 异步获取地区
getAreas(pid, callback) {
getAreaList({ pid: pid }).then(res=>{
if (res.code == 0) {
var data = [];
res.data.forEach((item, index) => {
data.push({
value: item.id,
label: item.name,
level: item.level
});
});
if (callback) callback(data);
}
})
}
}
};
</script>

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,166 @@
<template>
<view class="container loading1">
<view class="shape shape1"></view>
<view class="shape shape2"></view>
<view class="shape shape3"></view>
<view class="shape shape4"></view>
</view>
</template>
<script>
export default {
name: 'loading1',
data() {
return {};
}
}
</script>
<style scoped="true">
.container {
width: 30px;
height: 30px;
position: relative;
}
.container.loading1 {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
.container .shape {
position: absolute;
width: 10px;
height: 10px;
border-radius: 1px;
}
.container .shape.shape1 {
left: 0;
background-color: #1890FF;
}
.container .shape.shape2 {
right: 0;
background-color: #91CB74;
}
.container .shape.shape3 {
bottom: 0;
background-color: #FAC858;
}
.container .shape.shape4 {
bottom: 0;
right: 0;
background-color: #EE6666;
}
.loading1 .shape1 {
-webkit-animation: animation1shape1 0.5s ease 0s infinite alternate;
animation: animation1shape1 0.5s ease 0s infinite alternate;
}
@-webkit-keyframes animation1shape1 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(16px, 16px);
transform: translate(16px, 16px);
}
}
@keyframes animation1shape1 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(16px, 16px);
transform: translate(16px, 16px);
}
}
.loading1 .shape2 {
-webkit-animation: animation1shape2 0.5s ease 0s infinite alternate;
animation: animation1shape2 0.5s ease 0s infinite alternate;
}
@-webkit-keyframes animation1shape2 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(-16px, 16px);
transform: translate(-16px, 16px);
}
}
@keyframes animation1shape2 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(-16px, 16px);
transform: translate(-16px, 16px);
}
}
.loading1 .shape3 {
-webkit-animation: animation1shape3 0.5s ease 0s infinite alternate;
animation: animation1shape3 0.5s ease 0s infinite alternate;
}
@-webkit-keyframes animation1shape3 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(16px, -16px);
transform: translate(16px, -16px);
}
}
@keyframes animation1shape3 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(16px, -16px);
transform: translate(16px, -16px);
}
}
.loading1 .shape4 {
-webkit-animation: animation1shape4 0.5s ease 0s infinite alternate;
animation: animation1shape4 0.5s ease 0s infinite alternate;
}
@-webkit-keyframes animation1shape4 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(-16px, -16px);
transform: translate(-16px, -16px);
}
}
@keyframes animation1shape4 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(-16px, -16px);
transform: translate(-16px, -16px);
}
}
</style>

View File

@@ -0,0 +1,176 @@
<template>
<view class="container loading2">
<view class="shape shape1"></view>
<view class="shape shape2"></view>
<view class="shape shape3"></view>
<view class="shape shape4"></view>
</view>
</template>
<script>
export default {
name: 'loading2',
data() {
return {};
}
}
</script>
<style scoped="true">
.container {
width: 30px;
height: 30px;
position: relative;
}
.container.loading2 {
-webkit-transform: rotate(10deg);
transform: rotate(10deg);
}
.container.loading2 .shape {
border-radius: 5px;
}
.container.loading2 {
-webkit-animation: rotation 1s infinite;
animation: rotation 1s infinite;
}
.container .shape {
position: absolute;
width: 10px;
height: 10px;
border-radius: 1px;
}
.container .shape.shape1 {
left: 0;
background-color: #1890FF;
}
.container .shape.shape2 {
right: 0;
background-color: #91CB74;
}
.container .shape.shape3 {
bottom: 0;
background-color: #FAC858;
}
.container .shape.shape4 {
bottom: 0;
right: 0;
background-color: #EE6666;
}
.loading2 .shape1 {
-webkit-animation: animation2shape1 0.5s ease 0s infinite alternate;
animation: animation2shape1 0.5s ease 0s infinite alternate;
}
@-webkit-keyframes animation2shape1 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(20px, 20px);
transform: translate(20px, 20px);
}
}
@keyframes animation2shape1 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(20px, 20px);
transform: translate(20px, 20px);
}
}
.loading2 .shape2 {
-webkit-animation: animation2shape2 0.5s ease 0s infinite alternate;
animation: animation2shape2 0.5s ease 0s infinite alternate;
}
@-webkit-keyframes animation2shape2 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(-20px, 20px);
transform: translate(-20px, 20px);
}
}
@keyframes animation2shape2 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(-20px, 20px);
transform: translate(-20px, 20px);
}
}
.loading2 .shape3 {
-webkit-animation: animation2shape3 0.5s ease 0s infinite alternate;
animation: animation2shape3 0.5s ease 0s infinite alternate;
}
@-webkit-keyframes animation2shape3 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(20px, -20px);
transform: translate(20px, -20px);
}
}
@keyframes animation2shape3 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(20px, -20px);
transform: translate(20px, -20px);
}
}
.loading2 .shape4 {
-webkit-animation: animation2shape4 0.5s ease 0s infinite alternate;
animation: animation2shape4 0.5s ease 0s infinite alternate;
}
@-webkit-keyframes animation2shape4 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(-20px, -20px);
transform: translate(-20px, -20px);
}
}
@keyframes animation2shape4 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(-20px, -20px);
transform: translate(-20px, -20px);
}
}
</style>

View File

@@ -0,0 +1,182 @@
<template>
<view class="container loading3">
<view class="shape shape1"></view>
<view class="shape shape2"></view>
<view class="shape shape3"></view>
<view class="shape shape4"></view>
</view>
</template>
<script>
export default {
name: 'loading3',
data() {
return {};
}
}
</script>
<style scoped="true">
.container {
width: 30px;
height: 30px;
position: relative;
}
.container.loading3 {
-webkit-animation: rotation 1s infinite;
animation: rotation 1s infinite;
}
.container.loading3 .shape1 {
border-top-left-radius: 10px;
}
.container.loading3 .shape2 {
border-top-right-radius: 10px;
}
.container.loading3 .shape3 {
border-bottom-left-radius: 10px;
}
.container.loading3 .shape4 {
border-bottom-right-radius: 10px;
}
.container .shape {
position: absolute;
width: 10px;
height: 10px;
border-radius: 1px;
}
.container .shape.shape1 {
left: 0;
background-color: #1890FF;
}
.container .shape.shape2 {
right: 0;
background-color: #91CB74;
}
.container .shape.shape3 {
bottom: 0;
background-color: #FAC858;
}
.container .shape.shape4 {
bottom: 0;
right: 0;
background-color: #EE6666;
}
.loading3 .shape1 {
-webkit-animation: animation3shape1 0.5s ease 0s infinite alternate;
animation: animation3shape1 0.5s ease 0s infinite alternate;
}
@-webkit-keyframes animation3shape1 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(5px, 5px);
transform: translate(5px, 5px);
}
}
@keyframes animation3shape1 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(5px, 5px);
transform: translate(5px, 5px);
}
}
.loading3 .shape2 {
-webkit-animation: animation3shape2 0.5s ease 0s infinite alternate;
animation: animation3shape2 0.5s ease 0s infinite alternate;
}
@-webkit-keyframes animation3shape2 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(-5px, 5px);
transform: translate(-5px, 5px);
}
}
@keyframes animation3shape2 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(-5px, 5px);
transform: translate(-5px, 5px);
}
}
.loading3 .shape3 {
-webkit-animation: animation3shape3 0.5s ease 0s infinite alternate;
animation: animation3shape3 0.5s ease 0s infinite alternate;
}
@-webkit-keyframes animation3shape3 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(5px, -5px);
transform: translate(5px, -5px);
}
}
@keyframes animation3shape3 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(5px, -5px);
transform: translate(5px, -5px);
}
}
.loading3 .shape4 {
-webkit-animation: animation3shape4 0.5s ease 0s infinite alternate;
animation: animation3shape4 0.5s ease 0s infinite alternate;
}
@-webkit-keyframes animation3shape4 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(-5px, -5px);
transform: translate(-5px, -5px);
}
}
@keyframes animation3shape4 {
from {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
to {
-webkit-transform: translate(-5px, -5px);
transform: translate(-5px, -5px);
}
}
</style>

View File

@@ -0,0 +1,227 @@
<template>
<view class="container loading5">
<view class="shape shape1"></view>
<view class="shape shape2"></view>
<view class="shape shape3"></view>
<view class="shape shape4"></view>
</view>
</template>
<script>
export default {
name: 'loading5',
data() {
return {};
}
}
</script>
<style scoped="true">
.container {
width: 30px;
height: 30px;
position: relative;
}
.container.loading5 .shape {
width: 15px;
height: 15px;
}
.container .shape {
position: absolute;
width: 10px;
height: 10px;
border-radius: 1px;
}
.container .shape.shape1 {
left: 0;
background-color: #1890FF;
}
.container .shape.shape2 {
right: 0;
background-color: #91CB74;
}
.container .shape.shape3 {
bottom: 0;
background-color: #FAC858;
}
.container .shape.shape4 {
bottom: 0;
right: 0;
background-color: #EE6666;
}
.loading5 .shape1 {
animation: animation5shape1 2s ease 0s infinite reverse;
}
@-webkit-keyframes animation5shape1 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(0, 15px);
transform: translate(0, 15px);
}
50% {
-webkit-transform: translate(15px, 15px);
transform: translate(15px, 15px);
}
75% {
-webkit-transform: translate(15px, 0);
transform: translate(15px, 0);
}
}
@keyframes animation5shape1 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(0, 15px);
transform: translate(0, 15px);
}
50% {
-webkit-transform: translate(15px, 15px);
transform: translate(15px, 15px);
}
75% {
-webkit-transform: translate(15px, 0);
transform: translate(15px, 0);
}
}
.loading5 .shape2 {
animation: animation5shape2 2s ease 0s infinite reverse;
}
@-webkit-keyframes animation5shape2 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(-15px, 0);
transform: translate(-15px, 0);
}
50% {
-webkit-transform: translate(-15px, 15px);
transform: translate(-15px, 15px);
}
75% {
-webkit-transform: translate(0, 15px);
transform: translate(0, 15px);
}
}
@keyframes animation5shape2 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(-15px, 0);
transform: translate(-15px, 0);
}
50% {
-webkit-transform: translate(-15px, 15px);
transform: translate(-15px, 15px);
}
75% {
-webkit-transform: translate(0, 15px);
transform: translate(0, 15px);
}
}
.loading5 .shape3 {
animation: animation5shape3 2s ease 0s infinite reverse;
}
@-webkit-keyframes animation5shape3 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(15px, 0);
transform: translate(15px, 0);
}
50% {
-webkit-transform: translate(15px, -15px);
transform: translate(15px, -15px);
}
75% {
-webkit-transform: translate(0, -15px);
transform: translate(0, -15px);
}
}
@keyframes animation5shape3 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(15px, 0);
transform: translate(15px, 0);
}
50% {
-webkit-transform: translate(15px, -15px);
transform: translate(15px, -15px);
}
75% {
-webkit-transform: translate(0, -15px);
transform: translate(0, -15px);
}
}
.loading5 .shape4 {
animation: animation5shape4 2s ease 0s infinite reverse;
}
@-webkit-keyframes animation5shape4 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(0, -15px);
transform: translate(0, -15px);
}
50% {
-webkit-transform: translate(-15px, -15px);
transform: translate(-15px, -15px);
}
75% {
-webkit-transform: translate(-15px, 0);
transform: translate(-15px, 0);
}
}
@keyframes animation5shape4 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(0, -15px);
transform: translate(0, -15px);
}
50% {
-webkit-transform: translate(-15px, -15px);
transform: translate(-15px, -15px);
}
75% {
-webkit-transform: translate(-15px, 0);
transform: translate(-15px, 0);
}
}
</style>

View File

@@ -0,0 +1,235 @@
<template>
<view class="container loading6">
<view class="shape shape1"></view>
<view class="shape shape2"></view>
<view class="shape shape3"></view>
<view class="shape shape4"></view>
</view>
</template>
<script>
export default {
name: 'loading6',
data() {
return {};
}
}
</script>
<style scoped="true">
.container {
width: 30px;
height: 30px;
position: relative;
}
.container.loading6 {
-webkit-animation: rotation 1s infinite;
animation: rotation 1s infinite;
}
.container.loading6 .shape {
width: 12px;
height: 12px;
border-radius: 2px;
}
.container .shape {
position: absolute;
width: 10px;
height: 10px;
border-radius: 1px;
}
.container .shape.shape1 {
left: 0;
background-color: #1890FF;
}
.container .shape.shape2 {
right: 0;
background-color: #91CB74;
}
.container .shape.shape3 {
bottom: 0;
background-color: #FAC858;
}
.container .shape.shape4 {
bottom: 0;
right: 0;
background-color: #EE6666;
}
.loading6 .shape1 {
-webkit-animation: animation6shape1 2s linear 0s infinite normal;
animation: animation6shape1 2s linear 0s infinite normal;
}
@-webkit-keyframes animation6shape1 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(0, 18px);
transform: translate(0, 18px);
}
50% {
-webkit-transform: translate(18px, 18px);
transform: translate(18px, 18px);
}
75% {
-webkit-transform: translate(18px, 0);
transform: translate(18px, 0);
}
}
@keyframes animation6shape1 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(0, 18px);
transform: translate(0, 18px);
}
50% {
-webkit-transform: translate(18px, 18px);
transform: translate(18px, 18px);
}
75% {
-webkit-transform: translate(18px, 0);
transform: translate(18px, 0);
}
}
.loading6 .shape2 {
-webkit-animation: animation6shape2 2s linear 0s infinite normal;
animation: animation6shape2 2s linear 0s infinite normal;
}
@-webkit-keyframes animation6shape2 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(-18px, 0);
transform: translate(-18px, 0);
}
50% {
-webkit-transform: translate(-18px, 18px);
transform: translate(-18px, 18px);
}
75% {
-webkit-transform: translate(0, 18px);
transform: translate(0, 18px);
}
}
@keyframes animation6shape2 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(-18px, 0);
transform: translate(-18px, 0);
}
50% {
-webkit-transform: translate(-18px, 18px);
transform: translate(-18px, 18px);
}
75% {
-webkit-transform: translate(0, 18px);
transform: translate(0, 18px);
}
}
.loading6 .shape3 {
-webkit-animation: animation6shape3 2s linear 0s infinite normal;
animation: animation6shape3 2s linear 0s infinite normal;
}
@-webkit-keyframes animation6shape3 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(18px, 0);
transform: translate(18px, 0);
}
50% {
-webkit-transform: translate(18px, -18px);
transform: translate(18px, -18px);
}
75% {
-webkit-transform: translate(0, -18px);
transform: translate(0, -18px);
}
}
@keyframes animation6shape3 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(18px, 0);
transform: translate(18px, 0);
}
50% {
-webkit-transform: translate(18px, -18px);
transform: translate(18px, -18px);
}
75% {
-webkit-transform: translate(0, -18px);
transform: translate(0, -18px);
}
}
.loading6 .shape4 {
-webkit-animation: animation6shape4 2s linear 0s infinite normal;
animation: animation6shape4 2s linear 0s infinite normal;
}
@-webkit-keyframes animation6shape4 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(0, -18px);
transform: translate(0, -18px);
}
50% {
-webkit-transform: translate(-18px, -18px);
transform: translate(-18px, -18px);
}
75% {
-webkit-transform: translate(-18px, 0);
transform: translate(-18px, 0);
}
}
@keyframes animation6shape4 {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
25% {
-webkit-transform: translate(0, -18px);
transform: translate(0, -18px);
}
50% {
-webkit-transform: translate(-18px, -18px);
transform: translate(-18px, -18px);
}
75% {
-webkit-transform: translate(-18px, 0);
transform: translate(-18px, 0);
}
}
</style>

View File

@@ -0,0 +1,34 @@
<template>
<view>
<Loading1 v-if="loadingType==1" />
<Loading2 v-if="loadingType==2" />
<Loading3 v-if="loadingType==3" />
<Loading4 v-if="loadingType==4" />
<Loading5 v-if="loadingType==5" />
</view>
</template>
<script>
import Loading1 from "./loading1.vue";
import Loading2 from "./loading2.vue";
import Loading3 from "./loading3.vue";
import Loading4 from "./loading4.vue";
import Loading5 from "./loading5.vue";
export default {
components: {Loading1, Loading2, Loading3, Loading4, Loading5},
name: 'qiun-loading',
props: {
loadingType: {
type: Number,
default: 2
},
},
data() {
return {};
},
}
</script>
<style>
</style>

View File

@@ -0,0 +1,455 @@
<template>
<view class="uni-select-lay" :style="{ 'z-index': zindex }">
<input type="text" :name="name" v-model="value" class="uni-select-input" readonly />
<view class="uni-select-lay-select" :class="{ active: active }">
<!-- 禁用mask -->
<view class="uni-disabled" v-if="disabled"></view>
<!-- 禁用mask -->
<!-- 清空 -->
<view class="uni-select-lay-input-close" v-if="changevalue != '' && this.active"><text @click.stop="removevalue"></text></view>
<!-- 清空 -->
<input
type="text"
readonly
disabled="true"
class="uni-select-lay-input"
:class="{ active: changevalue != '' && changevalue != placeholder }"
v-model="changevalue"
:placeholder="placeholder"
@focus="unifocus"
@input="intchange"
@blur="uniblur"
@click.stop="select"
/>
<view class="uni-select-lay-icon" :class="{ disabled: disabled }" @click.stop="select"><text></text></view>
</view>
<view class="uni-date-mask" v-show="active" @click.stop="select"></view>
<scroll-view class="uni-select-lay-options" :scroll-y="true" v-show="active" @scroll="selectmove" @touchstart="movetouch">
<template v-if="!changes">
<view class="uni-select-lay-item" v-if="showplaceholder" :class="{ active: value == '' }" @click.stop="selectitem(-1, null)">{{ placeholder }}</view>
<view class="uni-select-lay-item" :class="{ active: value == item[svalue], disabled: item.disabled }" v-for="(item, index) in options" :key="index" @click.stop="selectitem(index, item)">
{{ item[slabel] }}
</view>
</template>
<!-- 搜索 -->
<template v-else>
<template v-if="vlist.length > 0">
<view class="uni-select-lay-item" :class="{ active: value == item[svalue] }" v-for="(item, index) in vlist" :key="index" @click.stop="selectitem(index, item)">
{{ item[slabel] }}
</view>
</template>
<template v-else>
<view class="nosearch">{{ changesValue }}</view>
</template>
</template>
</scroll-view>
</view>
</template>
<script>
export default {
name: 'select-lay',
props: {
disabled: {
type: Boolean,
default: false
},
zindex: {
type: Number,
default: 999
},
options: {
type: Array,
default() {
return [];
}
},
name: {
type: String,
default: ''
},
value: {
type: [String,Number],
default: ''
},
placeholder: {
type: String,
default: '请选择'
},
showplaceholder: {
type: Boolean,
default: true
},
slabel: {
type: String,
default: 'label'
},
svalue: {
type: String,
default: 'value'
}
},
data() {
return {
active: false, //组件是否激活,
isfocus: false, //是否有焦点
isremove: false, //是否是因为点击清空才导致的失去焦点
ismove: false, //是否是因为移动才失去焦点
changevalue: '', //搜索框同步
oldvalue: '', //数据回滚
changes: false, //正在搜索
changesValue: '',
vlist: [], //搜索框查询的列表
settimer: null //value改变定时器
};
},
mounted() {
this.itemcheck();
},
watch: {
//value改变
value() {
this.itemcheck();
},
//初始化数组
options() {
// 此处判断是否有初始value,存在则判断显示文字
this.itemcheck();
}
},
methods: {
//判断数组跟当前active值
itemcheck() {
// 此处判断是否有初始value,存在则判断显示文字
if (this.value != '') {
// 展示plachhoder
//判断数组
if (this.options.length > 0) {
this.options.forEach(item => {
if (this.value == item[this.svalue]) {
this.oldvalue = this.changevalue = item[this.slabel];
return;
}
});
}
} else {
this.oldvalue = this.changevalue = '';
}
},
//点击组件
select() {
if (this.disabled) return;
this.active = !this.active;
if (this.active) {
this.changes = false;
} else {
this.changevalue = this.oldvalue;
}
},
// 获得焦点
unifocus() {
if (this.disabled) return;
this.active = true;
this.changes = false;
this.isfocus = true;
},
// 失去焦点
uniblur() {
this.isfocus = false;
// bug 点击组件列会先触发失去焦点,此时组件列事件不执行
setTimeout(() => {
if (this.isremove || this.ismove) {
this.isremove = false;
this.ismove = false;
} else {
this.changevalue = this.oldvalue;
this.isremove = false;
this.active = false;
}
}, 153);
},
movetouch() {
setTimeout(() => {
if (this.isfocus) {
this.ismove = false;
return;
}
if (!this.ismove) this.ismove = true;
}, 100);
// this.changes = false;
},
selectmove() {
setTimeout(() => {
if (this.isfocus) {
this.ismove = false;
return;
}
if (!this.ismove) this.ismove = true;
}, 100);
// this.changes = false;
},
//移除数据
removevalue() {
this.isremove = true;
this.changes = false;
this.changevalue = '';
},
//value 改变
intchange() {
if (this.changevalue == '') {
this.changes = false;
return;
}
if (this.oldvalue == this.changevalue) {
return;
}
this.vlist = [];
this.changes = true;
this.changesValue = '正在搜索...';
if (this.settimer) {
clearTimeout(this.settimer);
}
this.settimer = setTimeout(() => {
this.vlist = this.options.filter(item => {
return item[this.slabel].includes(this.changevalue);
});
if (this.vlist.length === 0) {
this.changesValue = '暂无匹配内容!';
}
}, 600);
},
//点击组件列
selectitem(index, item) {
if (item && item.disabled) {
return false;
}
this.changevalue = this.oldvalue;
this.active = false;
this.$emit('selectitem', index, item);
}
}
};
</script>
<style lang="scss" scoped>
.uni-select-lay {
position: relative;
z-index: 999;
box-sizing: border-box;
.uni-select-input {
opacity: 0;
position: absolute;
z-index: -111;
}
// select部分
.uni-select-lay-select {
user-select: none;
position: relative;
z-index: 3;
height: 0.32rem;
padding: 0 0.3rem 0 0.1rem;
box-sizing: border-box;
border-radius: 0.02rem;
border: 0.01rem solid rgb(229, 229, 229);
display: flex;
align-items: center;
font-size: 0.14rem;
color: #999;
.uni-disabled {
position: absolute;
left: 0;
width: 100%;
height: 100%;
z-index: 19;
cursor: no-drop;
background: rgba(255, 255, 255, 0.5);
}
// input 框的清除按钮
.uni-select-lay-input-close {
position: absolute;
right: 0.35rem;
top: 0;
height: 100%;
width: 0.15rem;
display: flex;
align-items: center;
justify-content: center;
z-index: 3;
cursor: pointer;
text {
position: relative;
background: #fff;
width: 0.13rem;
height: 0.13rem;
border-radius: 50%;
border: 0.01rem solid #bbb;
&::before,
&::after {
content: '';
position: absolute;
left: 20%;
top: 50%;
height: 0.01rem;
width: 60%;
transform: rotate(45deg);
background-color: #bbb;
}
&::after {
transform: rotate(-45deg);
}
}
}
.uni-select-lay-input {
font-size: 0.14rem;
color: #999;
display: block;
width: 98%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 0.3rem;
box-sizing: border-box;
&.active {
color: #333;
}
}
.uni-select-lay-icon {
cursor: pointer;
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 0.3rem;
display: flex;
align-items: center;
justify-content: center;
&::before {
content: '';
width: 0.01rem;
height: 100%;
position: absolute;
left: 0;
top: 0;
background-color: #e5e5e5;
}
text {
display: block;
width: 0;
height: 0;
border-width: 0.07rem 0.07rem 0;
border-style: solid;
border-color: #bbb transparent transparent;
transition: 0.3s;
}
&.disabled {
cursor: no-drop;
text {
width: 0.2rem;
height: 0.2rem;
border: 0.02rem solid #ff0000;
border-radius: 50%;
transition: 0.3s;
position: relative;
z-index: 999;
&::after {
content: '';
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 0.02rem;
margin-top: -0.01rem;
background-color: #ff0000;
transform: rotate(45deg);
}
}
}
}
&.active .uni-select-lay-icon {
text {
transform: rotate(180deg);
}
}
}
// options部分
.uni-select-lay-options {
user-select: none;
position: absolute;
top: calc(100% + 0.05rem);
left: 0;
width: 100%;
// height: 500rpx;
max-height: 2.5rem;
// overflow-y: auto;
border-radius: 0.02rem;
border: 1px solid rgb(229, 229, 229);
background: #fff;
padding: 0.05rem 0;
box-sizing: border-box;
z-index: 9;
.uni-select-lay-item {
padding: 0 0.1rem;
box-sizing: border-box;
cursor: pointer;
line-height: 2.5;
transition: 0.3s;
font-size: 0.14rem;
&.active {
background: $primary-color;
color: #fff;
&:hover {
background: $primary-color;
color: #fff;
}
}
&.disabled {
color: #999;
cursor: not-allowed;
}
&:hover {
background-color: #f5f5f5;
}
}
.nosearch {
font-size: 0.16rem;
line-height: 3;
text-align: center;
color: #666;
}
}
}
.uni-date-mask {
position: fixed;
bottom: 0;
top: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0);
transition-duration: 0.3s;
z-index: 8;
}
</style>

View File

@@ -0,0 +1,480 @@
<template>
<unipopup ref="dialogRef" type="center" :maskClick="false">
<view class="stock-dialog-wrap">
<view class="stock-dialog-head">
<text>商品选择</text>
<text class="iconfont iconguanbi1" @click="$emit('change', false)"></text>
</view>
<view class="stock-dialog-body">
<view class="tree">
<scroll-view scroll-y="true" class="list-wrap">
<view class="item" :class="{ 'active': option.category_id === '' }" @click="itemClick({ category_id: '', child_num: 0 })">
<view class="icon"></view>
<view>全部分类</view>
</view>
<view v-for="(item, key) in goodsCategoryList" :key="key">
<view class="item" :class="{ 'active': option.category_id === item.category_id }" @click="itemClick(item)">
<view class="icon" :class="{ 'active': activeList.indexOf(item.category_id) != -1 }">
<text v-if="item.child_num" class="iconfont iconsanjiao_xia"></text>
</view>
<view>{{ item.title }}</view>
</view>
<template v-if="item.child_num">
<view v-show="activeList.indexOf(item.category_id) != -1" v-for="(item2, key2) in item.children" :key="key2" class="level">
<view class="item" :class="{ 'active': option.category_id === item2.category_id }" @click="itemClick(item2)">
<view class="icon" :class="{ 'active': activeList.indexOf(item2.category_id) != -1 }">
<text v-if="item2.child_num" class="iconfont iconsanjiao_xia"></text>
</view>
<view>{{ item2.title }}</view>
</view>
<template>
<view v-show="activeList.indexOf(item2.category_id) != -1" v-for="(item3, key3) in item2.children" :key="key3" class="level">
<view class="item item2" @click="itemClick(item3)">
<view class="icon"></view>
<view>{{ item3.title }}</view>
</view>
</view>
</template>
</view>
</template>
</view>
</scroll-view>
</view>
<view class="stock-dialog-table">
<view class="search common-form">
<view class="common-form-item">
<view class="form-inline">
<view class="form-input-inline">
<input type="text" v-model="option.search_text" @confirm="getStoreGoods" placeholder="请输入产品名称/规格/编码" class="form-input" />
</view>
</view>
<view class="form-inline common-btn-wrap">
<button type="default" class="screen-btn" @click="getStoreGoods">筛选</button>
</view>
</view>
</view>
<uniDataTable :url="url" :option="option" :cols="cols" :pagesize="8" ref="goodsListTable" @checkBox="checkBox" @tableData="tableDataChange"></uniDataTable>
</view>
</view>
<view class="btn">
<button type="primary" class="default-btn submit" @click="submit('close')">选中</button>
<button type="primary" class="default-btn" @click="$emit('change', false)">取消</button>
</view>
</view>
</unipopup>
</template>
<script>
import unipopup from '@/components/uni-popup/uni-popup.vue';
import uniDataTable from '@/components/uni-data-table/uni-data-table.vue';
import {getManageGoodsCategory} from '@/api/goods.js';
export default {
name: 'stockDialog',
components: {
unipopup,
uniDataTable
},
model: {
prop: 'value',
event: 'change'
},
props: {
value: {
type: Boolean,
default: false
},
params: {
type: Object,
default: ()=>{
return {}
}
},
apiType: {
type: String,
default: 'sku' //选择是sku 还是 spu
}
},
data() {
return {
goodsCategoryList: {},
activeList: [],//下拉激活
option: {
category_id: '',
search_text: '',
is_weigh: 0,
page_size: 8,
},
cols: [{
width: 6,
align: 'center',
checkbox: true
}, {
field: 'account_data',
width: 50,
title: '商品信息',
align: 'left',
templet: data => {
let img = this.$util.img(data.sku_image);
let html = `
<view class="goods-content">
<image class="goods-img" src="${img}" mode="aspectFit"/>
<text class="goods-name multi-hidden" title="${data.sku_name}">${data.sku_name}</text>
</view>
`;
return html;
}
}, {
field: 'real_stock',
width: 22,
title: '库存',
align: 'center',
templet: data => {
return (data.real_stock || 0);
}
}, {
width: 22,
title: '单位',
templet: data => {
return (data.unit || '件');
}
}],
checkList: {},
url: '/stock/storeapi/manage/getStoreGoods'
}
},
watch: {
value: {
handler: function (val) {
if (val) {
this.$nextTick(() => {
this.option = Object.assign(this.option, this.params);
if (this.params.temp_store_id && this.params.temp_store_id == '') {
delete this.option.temp_store_id
}
this.$refs.dialogRef.open()
})
} else {
this.$nextTick(() => {
this.option = Object(this.option, {
category_id: '',
search_text: '',
is_weigh: 0,
page: 1,
page_size: 8,
});
this.checkList = {};
this.$refs.dialogRef.close()
})
}
},
immediate: true
},
apiType: {
handler: function (val) {
if(val == 'sku'){
this.cols = [{
width: 6,
align: 'center',
checkbox: true
}, {
field: 'account_data',
width: 50,
title: '商品信息',
align: 'left',
templet: data => {
let img = this.$util.img(data.sku_image);
let html = `
<view class="goods-content">
<image class="goods-img" src="${img}" mode="aspectFit"/>
<text class="goods-name multi-hidden" title="${data.sku_name}">${data.sku_name}</text>
</view>
`;
return html;
}
}, {
field: 'real_stock',
width: 22,
title: '库存',
align: 'center',
templet: data => {
return (data.real_stock || 0);
}
}, {
width: 22,
title: '单位',
templet: data => {
return (data.unit || '件');
}
}];
this.url = '/stock/storeapi/manage/getStoreGoods';
}else if(val == 'spu'){
this.cols = [{
width: 6,
align: 'center',
checkbox: true
}, {
field: 'account_data',
width: 50,
title: '商品信息',
align: 'left',
templet: data => {
let img = this.$util.img(data.goods_image);
let html = `
<view class="goods-content">
<image class="goods-img" src="${img}" mode="aspectFit"/>
<text class="goods-name multi-hidden" title="${data.goods_name}">${data.goods_name}</text>
</view>
`;
return html;
}
}, {
field: 'goods_stock',
width: 22,
title: '库存',
align: 'center',
templet: data => {
return (data.goods_stock || 0);
}
}, {
width: 22,
title: '商品类型',
templet: data => {
return (data.goods_class_name || '--');
}
}];
this.url = '/cashier/storeapi/goods/getGoodsListBySelect';
}
},
immediate: true
}
},
mounted() {
this.getGoodsCategory()
},
methods: {
getGoodsCategory() {
getManageGoodsCategory().then(res=>{
uni.hideLoading();
if (res.data && Object.keys(res.data)) {
this.goodsCategoryList = res.data
} else {
this.$util.showToast({
title: res.message
});
}
})
},
itemClick(item) {//tree点击
this.option.category_id = item.category_id;
var index = this.activeList.indexOf(item.category_id);
if (item.child_num && index === -1) {
this.activeList.push(item.category_id);
} else if (item.child_num && index != -1) {
this.activeList.splice(index, 1);
}
this.$forceUpdate();
this.getStoreGoods();
},
getStoreGoods() {//表格查询
this.$refs.goodsListTable.load({
page: 1
});
},
checkBox(list, listIndex) {
this.checkList[this.$refs.goodsListTable.page] = {};
this.checkList[this.$refs.goodsListTable.page].data = list;
this.checkList[this.$refs.goodsListTable.page].index = listIndex;
},
tableDataChange(){
if(this.checkList[this.$refs.goodsListTable.page])
this.$refs.goodsListTable.defaultSelectData(this.checkList[this.$refs.goodsListTable.page].data, this.checkList[this.$refs.goodsListTable.page].index);
},
submit(val) {
if (!Object.values(this.checkList).length) {
this.$util.showToast({
title: '请选择商品'
});
return false
}
let data = [];
Object.values(this.checkList).forEach((item,index)=>{
data = data.concat(item.data)
});
this.$emit('selectGoods', data);
if(val !='submit'){
this.$emit('change', false);
}else{
this.$refs.goodsListTable.clearCheck();
}
this.checkList = [];
}
}
}
</script>
<style lang="scss" scoped>
.stock-dialog-wrap {
background-color: #fff;
border-radius: 0.05rem;
width: 9rem;
.stock-dialog-head {
padding: 0 0.15rem;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 0.15rem;
height: 0.45rem;
border-bottom: 0.01rem solid #e8eaec;
.iconguanbi1 {
font-size: $uni-font-size-lg;
}
}
.stock-dialog-body {
width: 100%;
height: 7.3rem;
padding: 0.1rem 0.2rem 0 0.2rem;
box-sizing: border-box;
display: flex;
.tree {
width: 1.8rem;
height: 7.1rem;
overflow-y: auto;
border-right: 0.01rem solid #e8eaec;
flex-shrink: 0;
flex-basis: auto;
flex-grow: 0;
box-sizing: border-box;
.list-wrap {
width: 100%;
height: 100%;
>view {
box-sizing: border-box;
width: 100%;
}
view.item {
display: flex;
align-items: center;
width: 100%;
box-sizing: border-box;
line-height: 0.3rem;
min-height: 0.3rem;
font-weight: 500;
&.active {
.icon,
view {
color: $primary-color !important;
}
background-color: #f7f7f7;
}
&:hover {
background-color: #f7f7f7;
}
.icon {
width: 0.2rem;
height: 0.3rem;
display: flex;
align-items: center;
justify-content: center;
transform: rotate(-90deg);
transition: all ease 0.5s;
&.active {
transform: rotate(-45deg);
}
}
}
.level {
width: 100%;
box-sizing: border-box;
.item {
padding-left: 0.2rem;
}
.item2 {
padding-left: 0.4rem;
}
}
}
}
.stock-dialog-table {
width: 6.6rem;
margin-left: 0.2rem;
.search {
display: flex;
justify-content: flex-end;
}
}
}
.btn {
display: flex;
justify-content: flex-end;
border-top: 0.01rem solid #e8eaec;
padding: 0.1rem 0.2rem 0.1rem 0.2rem;
height: 0.38rem;
.default-btn,
.primary-btn {
margin: 0;
}
.default-btn {
border: 0.01rem solid #e8eaec !important;
}
.submit{
margin-right: 0.15rem;
}
.default-btn::after {
display: none;
}
}
.common-form .common-btn-wrap {
margin-left: 0;
}
.common-form .common-btn-wrap .screen-btn {
margin-right: 0;
}
.common-form .common-form-item {
margin-bottom: 0.1rem;
}
/deep/ .goods-content {
display: flex;
.goods-img {
margin-right: 0.1rem;
width: 0.5rem;
height: 0.5rem;
flex-shrink: 0;
flex-basis: auto;
flex-grow: 0;
}
}
}
</style>

View File

@@ -0,0 +1,420 @@
/*
* uCharts®
* 高性能跨平台图表库支持H5、APP、小程序微信/支付宝/百度/头条/QQ/360、Vue、Taro等支持canvas的框架平台
* Copyright (c) 2021 QIUN®秋云 https://www.ucharts.cn All rights reserved.
* Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
* 复制使用请保留本段注释,感谢支持开源!
*
* uCharts®官方网站
* https://www.uCharts.cn
*
* 开源地址:
* https://gitee.com/uCharts/uCharts
*
* uni-app插件市场地址
* http://ext.dcloud.net.cn/plugin?id=271
*
*/
// 通用配置项
// 主题颜色配置如每个图表类型需要不同主题请在对应图表类型上更改color属性
const color = ['#1890FF', '#91CB74', '#FAC858', '#EE6666', '#73C0DE', '#3CA272', '#FC8452', '#9A60B4', '#ea7ccc'];
module.exports = {
//demotype为自定义图表类型
"type": ["pie", "ring", "rose", "funnel", "line", "column", "area", "radar", "gauge","candle","demotype"],
//增加自定义图表类型如果需要categories请在这里加入您的图表类型例如最后的"demotype"
"categories": ["line", "column", "area", "radar", "gauge", "candle","demotype"],
//instance为实例变量承载属性option为eopts承载属性不要删除
"instance": {},
"option": {},
//下面是自定义format配置因除H5端外的其他端无法通过props传递函数只能通过此属性对应下标的方式来替换
"formatter":{
"tooltipDemo1":function(res){
let result = ''
for (let i in res) {
if (i == 0) {
result += res[i].axisValueLabel + '年销售额'
}
let value = '--'
if (res[i].data !== null) {
value = res[i].data
}
// #ifdef H5
result += '\n' + res[i].seriesName + '' + value + ' 万元'
// #endif
// #ifdef APP-PLUS
result += '<br/>' + res[i].marker + res[i].seriesName + '' + value + ' 万元'
// #endif
}
return result;
},
legendFormat:function(name){
return "自定义图例+"+name;
},
yAxisFormatDemo:function (value, index) {
return value + '元';
},
seriesFormatDemo:function(res){
return res.name + '年' + res.value + '元';
}
},
//这里演示了自定义您的图表类型的option可以随意命名之后在组件上 type="demotype" 后组件会调用这个花括号里的option如果组件上还存在eopts参数会将demotype与eopts中option合并后渲染图表。
"demotype":{
"color": color,
//在这里填写echarts的option即可
},
//下面是自定义配置,请添加项目所需的通用配置
"column": {
"color": color,
"title": {
"text": ''
},
"tooltip": {
"trigger": 'axis'
},
"grid": {
"top": 30,
"bottom": 50,
"right": 15,
"left": 40
},
"legend": {
"bottom": 'left',
},
"toolbox": {
"show": false,
},
"xAxis": {
"type": 'category',
"axisLabel": {
"color": '#666666'
},
"axisLine": {
"lineStyle": {
"color": '#CCCCCC'
}
},
"boundaryGap": true,
"data": []
},
"yAxis": {
"type": 'value',
"axisTick": {
"show": false,
},
"axisLabel": {
"color": '#666666'
},
"axisLine": {
"lineStyle": {
"color": '#CCCCCC'
}
},
},
"seriesTemplate": {
"name": '',
"type": 'bar',
"data": [],
"barwidth": 20,
"label": {
"show": true,
"color": "#666666",
"position": 'top',
},
},
},
"line": {
"color": color,
"title": {
"text": ''
},
"tooltip": {
"trigger": 'axis'
},
"grid": {
"top": 30,
"bottom": 50,
"right": 15,
"left": 40
},
"legend": {
"bottom": 'left',
},
"toolbox": {
"show": false,
},
"xAxis": {
"type": 'category',
"axisLabel": {
"color": '#666666'
},
"axisLine": {
"lineStyle": {
"color": '#CCCCCC'
}
},
"boundaryGap": true,
"data": []
},
"yAxis": {
"type": 'value',
"axisTick": {
"show": false,
},
"axisLabel": {
"color": '#666666'
},
"axisLine": {
"lineStyle": {
"color": '#CCCCCC'
}
},
},
"seriesTemplate": {
"name": '',
"type": 'line',
"data": [],
"barwidth": 20,
"label": {
"show": true,
"color": "#666666",
"position": 'top',
},
},
},
"area": {
"color": color,
"title": {
"text": ''
},
"tooltip": {
"trigger": 'axis'
},
"grid": {
"top": 30,
"bottom": 50,
"right": 15,
"left": 40
},
"legend": {
"bottom": 'left',
},
"toolbox": {
"show": false,
},
"xAxis": {
"type": 'category',
"axisLabel": {
"color": '#666666'
},
"axisLine": {
"lineStyle": {
"color": '#CCCCCC'
}
},
"boundaryGap": true,
"data": []
},
"yAxis": {
"type": 'value',
"axisTick": {
"show": false,
},
"axisLabel": {
"color": '#666666'
},
"axisLine": {
"lineStyle": {
"color": '#CCCCCC'
}
},
},
"seriesTemplate": {
"name": '',
"type": 'line',
"data": [],
"areaStyle": {},
"label": {
"show": true,
"color": "#666666",
"position": 'top',
},
},
},
"pie": {
"color": color,
"title": {
"text": ''
},
"tooltip": {
"trigger": 'item'
},
"grid": {
"top": 40,
"bottom": 30,
"right": 15,
"left": 15
},
"legend": {
"bottom": 'left',
},
"seriesTemplate": {
"name": '',
"type": 'pie',
"data": [],
"radius": '50%',
"label": {
"show": true,
"color": "#666666",
"position": 'top',
},
},
},
"ring": {
"color": color,
"title": {
"text": ''
},
"tooltip": {
"trigger": 'item'
},
"grid": {
"top": 40,
"bottom": 30,
"right": 15,
"left": 15
},
"legend": {
"bottom": 'left',
},
"seriesTemplate": {
"name": '',
"type": 'pie',
"data": [],
"radius": ['40%', '70%'],
"avoidLabelOverlap": false,
"label": {
"show": true,
"color": "#666666",
"position": 'top',
},
"labelLine": {
"show": true
},
},
},
"rose": {
"color": color,
"title": {
"text": ''
},
"tooltip": {
"trigger": 'item'
},
"legend": {
"top": 'bottom'
},
"seriesTemplate": {
"name": '',
"type": 'pie',
"data": [],
"radius": "55%",
"center": ['50%', '50%'],
"rosetype": 'area',
},
},
"funnel": {
"color": color,
"title": {
"text": ''
},
"tooltip": {
"trigger": 'item',
"formatter": "{b} : {c}%"
},
"legend": {
"top": 'bottom'
},
"seriesTemplate": {
"name": '',
"type": 'funnel',
"left": '10%',
"top": 60,
"bottom": 60,
"width": '80%',
"min": 0,
"max": 100,
"minSize": '0%',
"maxSize": '100%',
"sort": 'descending',
"gap": 2,
"label": {
"show": true,
"position": 'inside'
},
"labelLine": {
"length": 10,
"lineStyle": {
"width": 1,
"type": 'solid'
}
},
"itemStyle": {
"bordercolor": '#fff',
"borderwidth": 1
},
"emphasis": {
"label": {
"fontSize": 20
}
},
"data": [],
},
},
"gauge": {
"color": color,
"tooltip": {
"formatter": '{a} <br/>{b} : {c}%'
},
"seriesTemplate": {
"name": '业务指标',
"type": 'gauge',
"detail": {"formatter": '{value}%'},
"data": [{"value": 50, "name": '完成率'}]
},
},
"candle": {
"xAxis": {
"data": []
},
"yAxis": {},
"color": color,
"title": {
"text": ''
},
"dataZoom": [{
"type": 'inside',
"xAxisIndex": [0, 1],
"start": 10,
"end": 100
},
{
"show": true,
"xAxisIndex": [0, 1],
"type": 'slider',
"bottom": 10,
"start": 10,
"end": 100
}
],
"seriesTemplate": {
"name": '',
"type": 'k',
"data": [],
},
}
}

View File

@@ -0,0 +1,433 @@
/*
* uCharts®
* 高性能跨平台图表库支持H5、APP、小程序微信/支付宝/百度/头条/QQ/360、Vue、Taro等支持canvas的框架平台
* Copyright (c) 2021 QIUN®秋云 https://www.ucharts.cn All rights reserved.
* Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
* 复制使用请保留本段注释,感谢支持开源!
*
* uCharts®官方网站
* https://www.uCharts.cn
*
* 开源地址:
* https://gitee.com/uCharts/uCharts
*
* uni-app插件市场地址
* http://ext.dcloud.net.cn/plugin?id=271
*
*/
// 主题颜色配置如每个图表类型需要不同主题请在对应图表类型上更改color属性
const color = ['#1890FF', '#91CB74', '#FAC858', '#EE6666', '#73C0DE', '#3CA272', '#FC8452', '#9A60B4', '#ea7ccc'];
module.exports = {
//demotype为自定义图表类型
"type":["pie","ring","rose","word","funnel","map","arcbar","line","column","area","radar","gauge","candle","mix","demotype"],
"range":["饼状图","圆环图","玫瑰图","词云图","漏斗图","地图","圆弧进度条","折线图","柱状图","区域图","雷达图","仪表盘","K线图","混合图","自定义类型"],
//增加自定义图表类型如果需要categories请在这里加入您的图表类型例如最后的"demotype"
"categories":["line","column","area","radar","gauge","candle","mix","demotype"],
//instance为实例变量承载属性option为eopts承载属性不要删除
"instance":{},
"option":{},
//下面是自定义format配置因除H5端外的其他端无法通过props传递函数只能通过此属性对应下标的方式来替换
"formatter":{
"yAxisDemo1":function(val){return val+'元'},
"yAxisDemo2":function(val){return val.toFixed(2)},
"seriesDemo1":function(val){
return val+'元'
},
"tooltipDemo1":function(item, category, index, opts){
if(index==0){
return '随便用'+item.data+'年'
}else{
return '其他我没改'+item.data+'天'
}
},
"pieDemo":function(val, index, series){
if(index !== undefined){
return series[index].name+''+series[index].data+'元'
}
},
},
//这里演示了自定义您的图表类型的option可以随意命名之后在组件上 type="demotype" 后组件会调用这个花括号里的option如果组件上还存在opts参数会将demotype与opts中option合并后渲染图表。
"demotype":{
//我这里把曲线图当做了自定义图表类型,您可以根据需要随意指定类型或配置
"type": "line",
"color": color,
"padding": [15,10,0,15],
"xAxis": {
"disableGrid": true,
},
"yAxis": {
"gridType": "dash",
"dashLength": 2,
},
"legend": {
},
"extra": {
"line": {
"type": "curve",
"width": 2
},
}
},
//下面是自定义配置,请添加项目所需的通用配置
"pie":{
"type": "pie",
"color": color,
"padding": [5,5,5,5],
"extra": {
"pie": {
"activeOpacity": 0.5,
"activeRadius": 10,
"offsetAngle": 0,
"labelWidth": 15,
"border": true,
"borderWidth": 3,
"borderColor": "#FFFFFF"
},
}
},
"ring":{
"type": "ring",
"color": color,
"padding": [5,5,5,5],
"rotate": false,
"dataLabel": true,
"legend": {
"show": true,
"position": "right",
"lineHeight": 25,
},
"title": {
"name": "收益率",
"fontSize": 15,
"color": "#666666"
},
"subtitle": {
"name": "70%",
"fontSize": 25,
"color": "#7cb5ec"
},
"extra": {
"ring": {
"ringWidth":30,
"activeOpacity": 0.5,
"activeRadius": 10,
"offsetAngle": 0,
"labelWidth": 15,
"border": true,
"borderWidth": 3,
"borderColor": "#FFFFFF"
},
},
},
"rose":{
"type": "rose",
"color": color,
"padding": [5,5,5,5],
"legend": {
"show": true,
"position": "left",
"lineHeight": 25,
},
"extra": {
"rose": {
"type": "area",
"minRadius": 50,
"activeOpacity": 0.5,
"activeRadius": 10,
"offsetAngle": 0,
"labelWidth": 15,
"border": false,
"borderWidth": 2,
"borderColor": "#FFFFFF"
},
}
},
"word":{
"type": "word",
"color": color,
"extra": {
"word": {
"type": "normal",
"autoColors": false
}
}
},
"funnel":{
"type": "funnel",
"color": color,
"padding": [15,15,0,15],
"extra": {
"funnel": {
"activeOpacity": 0.3,
"activeWidth": 10,
"border": true,
"borderWidth": 2,
"borderColor": "#FFFFFF",
"fillOpacity": 1,
"labelAlign": "right"
},
}
},
"map":{
"type": "map",
"color": color,
"padding": [0,0,0,0],
"dataLabel": true,
"extra": {
"map": {
"border": true,
"borderWidth": 1,
"borderColor": "#666666",
"fillOpacity": 0.6,
"activeBorderColor": "#F04864",
"activeFillColor": "#FACC14",
"activeFillOpacity": 1
},
}
},
"arcbar":{
"type": "arcbar",
"color": color,
"title": {
"name": "百分比",
"fontSize": 25,
"color": "#00FF00"
},
"subtitle": {
"name": "默认标题",
"fontSize": 15,
"color": "#666666"
},
"extra": {
"arcbar": {
"type": "default",
"width": 12,
"backgroundColor": "#E9E9E9",
"startAngle": 0.75,
"endAngle": 0.25,
"gap": 2
}
}
},
"line":{
"type": "line",
"color": color,
"padding": [15,10,0,15],
"xAxis": {
"disableGrid": true,
},
"yAxis": {
"gridType": "dash",
"dashLength": 2,
},
"legend": {
},
"extra": {
"line": {
"type": "straight",
"width": 2
},
}
},
"column":{
"type": "column",
"color": color,
"padding": [15,15,0,5],
"xAxis": {
"disableGrid": true,
},
"yAxis": {
},
"legend": {
},
"extra": {
"column": {
"type": "group",
"width": 30,
"meterBorde": 1,
"meterFillColor": "#FFFFFF",
"activeBgColor": "#000000",
"activeBgOpacity": 0.08
},
}
},
"area":{
"type": "area",
"color": color,
"padding": [15,15,0,15],
"xAxis": {
"disableGrid": true,
},
"yAxis": {
"gridType": "dash",
"dashLength": 2,
},
"legend": {
},
"extra": {
"area": {
"type": "straight",
"opacity": 0.2,
"addLine": true,
"width": 2,
"gradient": false
},
}
},
"radar":{
"type": "radar",
"color": color,
"padding": [5,5,5,5],
"legend": {
"show": true,
"position": "right",
"lineHeight": 25,
},
"extra": {
"radar": {
"gridType": "radar",
"gridColor": "#CCCCCC",
"gridCount": 3,
"opacity": 0.2,
"labelColor": "#666666",
"max": 200
},
}
},
"gauge":{
"type": "gauge",
"color": color,
"title": {
"name": "66Km/H",
"fontSize": 25,
"color": "#2fc25b",
"offsetY": 50
},
"subtitle": {
"name": "实时速度",
"fontSize": 15,
"color": "#1890ff",
"offsetY": -50
},
"extra": {
"gauge": {
"type": "default",
"width": 30,
"labelColor": "#666666",
"startAngle": 0.75,
"endAngle": 0.25,
"startNumber": 0,
"endNumber": 100,
"labelFormat": "",
"splitLine": {
"fixRadius": 0,
"splitNumber": 10,
"width": 30,
"color": "#FFFFFF",
"childNumber": 5,
"childWidth": 12
},
"pointer": {
"width": 24,
"color": "auto"
}
}
}
},
"candle":{
"type": "candle",
"color": color,
"padding": [15,15,0,15],
"enableScroll": true,
"enableMarkLine": true,
"dataLabel": false,
"xAxis": {
"labelCount": 4,
"itemCount": 40,
"disableGrid": true,
"gridColor": "#CCCCCC",
"gridType": "solid",
"dashLength": 4,
"scrollShow": true,
"scrollAlign": "left",
"scrollColor": "#A6A6A6",
"scrollBackgroundColor": "#EFEBEF"
},
"yAxis": {
},
"legend": {
},
"extra": {
"candle": {
"color": {
"upLine": "#f04864",
"upFill": "#f04864",
"downLine": "#2fc25b",
"downFill": "#2fc25b"
},
"average": {
"show": true,
"name": ["MA5","MA10","MA30"],
"day": [5,10,20],
"color": ["#1890ff","#2fc25b","#facc14"]
}
},
"markLine": {
"type": "dash",
"dashLength": 5,
"data": [
{
"value": 2150,
"lineColor": "#f04864",
"showLabel": true
},
{
"value": 2350,
"lineColor": "#f04864",
"showLabel": true
}
]
}
}
},
"mix":{
"type": "mix",
"color": color,
"padding": [15,15,0,15],
"xAxis": {
"disableGrid": true,
},
"yAxis": {
"disabled": false,
"disableGrid": false,
"splitNumber": 5,
"gridType": "dash",
"dashLength": 4,
"gridColor": "#CCCCCC",
"padding": 10,
"showTitle": true,
"data": []
},
"legend": {
},
"extra": {
"mix": {
"column": {
"width": 20
}
},
}
},
"point":{
"type": "point",
"color":color,
"padding":[15,15,0,15],
},
"bubble":{
"type": "bubble",
"color":color,
"padding":[15,15,0,15],
}
}

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,12 @@
# uCharts JSSDK说明
1、如不使用uCharts组件可直接引用u-charts.js打包编译后会`自动压缩`,压缩后体积约为`98kb`
2、如果100kb的体积仍需压缩请手动删除u-charts.js内您不需要的图表类型如k线图candle。
3、config-ucharts.js为uCharts组件的用户配置文件升级前请`自行备份config-ucharts.js`文件,以免被强制覆盖。
3、config-echarts.js为ECharts组件的用户配置文件升级前请`自行备份config-echarts.js`文件,以免被强制覆盖。
# v1.0转v2.0注意事项
1、opts.colors变更为opts.color
2、ring圆环图的扩展配置由extra.pie变更为extra.ring
3、混合图借用的扩展配置由extra.column变更为extra.mix.column
4、全部涉及到format的格式化属性变更为formatter
5、不需要再传canvasId及$this参数如果通过uChats获取context可能会导致this实例混乱导致小程序开发者工具报错。如果不使用qiun-data-charts官方组件需要在new uCharts()实例化之前自行获取canvas的上下文contextctx并传入new中的contextopts.context。为了能跨更多的端给您带来的不便敬请谅解。

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,808 @@
<template>
<view class="uni-data-checklist" :style="{'margin-top':isTop+'px'}">
<template >
<checkbox-group v-if="multiple" class="checklist-group" :class="{'is-list':mode==='list' || wrap}" @change="chagne">
<label class="checklist-box" :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']"
:style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index">
<checkbox class="hidden" hidden :disabled="disabled || !!item.disabled" :value="item[map.value]+''" :checked="item.selected" />
<view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="checkbox__inner" :style="item.styleIcon">
<view class="checkbox__inner-icon"></view>
</view>
<view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}">
<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
<view v-if="mode === 'list' && icon === 'right'" class="checkobx__list" :style="item.styleBackgroud"></view>
</view>
</label>
</checkbox-group>
<radio-group v-else class="checklist-group" :class="{'is-list':mode==='list','is-wrap':wrap}" @change="chagne">
<label class="checklist-box" :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']"
:style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index">
<radio class="hidden" hidden :disabled="disabled || item.disabled" :value="item[map.value]+''" :checked="item.selected" />
<view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="radio__inner" :style="item.styleBackgroud">
<view class="radio__inner-icon" :style="item.styleIcon"></view>
</view>
<view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}">
<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
<view v-if="mode === 'list' && icon === 'right'" :style="item.styleRightIcon" class="checkobx__list"></view>
</view>
</label>
</radio-group>
</template>
</view>
</template>
<script>
/**
* DataChecklist 数据选择器
* @description 通过数据渲染 checkbox 和 radio
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
* @property {String} mode = [default| list | button | tag] 显示模式
* @value default 默认横排模式
* @value list 列表模式
* @value button 按钮模式
* @value tag 标签模式
* @property {Boolean} multiple = [true|false] 是否多选
* @property {Array|String|Number} value 默认值
* @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}]
* @property {Number|String} min 最小选择个数 multiple为true时生效
* @property {Number|String} max 最大选择个数 multiple为true时生效
* @property {Boolean} wrap 是否换行显示
* @property {String} icon = [left|right] list 列表模式下icon显示位置
* @property {Boolean} selectedColor 选中颜色
* @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效
* @property {Boolean} selectedTextColor 选中文本颜色,如不填写则自动显示
* @property {Object} map 字段映射, 默认 map={text:'text',value:'value'}
* @value left 左侧显示
* @value right 右侧显示
* @event {Function} change 选中发生变化触发
*/
export default {
name: 'uniDataChecklist',
mixins: [uniCloud.mixinDatacom || {}],
emits:['input','update:modelValue','change'],
props: {
mode: {
type: String,
default: 'default'
},
multiple: {
type: Boolean,
default: false
},
value: {
type: [Array, String, Number],
default () {
return ''
}
},
// TODO vue3
modelValue: {
type: [Array, String, Number],
default() {
return '';
}
},
localdata: {
type: Array,
default () {
return []
}
},
min: {
type: [Number, String],
default: ''
},
max: {
type: [Number, String],
default: ''
},
wrap: {
type: Boolean,
default: false
},
icon: {
type: String,
default: 'left'
},
selectedColor: {
type: String,
default: ''
},
selectedTextColor: {
type: String,
default: ''
},
emptyText:{
type: String,
default: '暂无数据'
},
disabled:{
type: Boolean,
default: false
},
map:{
type: Object,
default(){
return {
text:'text',
value:'value'
}
}
}
},
watch: {
localdata: {
handler(newVal) {
this.range = newVal;
this.dataList = this.getDataList(this.getSelectedValue(newVal));
},
deep: true
},
mixinDatacomResData(newVal) {
this.range = newVal;
this.dataList = this.getDataList(this.getSelectedValue(newVal));
},
value(newVal) {
this.dataList = this.getDataList(newVal);
// fix by mehaotian is_reset 在 uni-forms 中定义
if(!this.is_reset){
this.is_reset = false;
this.formItem && this.formItem.setValue(newVal);
}
},
modelValue(newVal) {
this.dataList = this.getDataList(newVal);
if(!this.is_reset){
this.is_reset = false;
this.formItem && this.formItem.setValue(newVal);
}
}
},
data() {
return {
dataList: [],
range: [],
contentText: {
contentdown: '查看更多',
contentrefresh: '加载中',
contentnomore: '没有更多'
},
isLocal:true,
styles: {
selectedColor: '$primary-color',
selectedTextColor: '#666',
},
isTop:0
};
},
computed:{
dataValue(){
if(this.value === '')return this.modelValue;
if(this.modelValue === '') return this.value;
return this.value;
}
},
created() {
this.form = this.getForm('uniForms');
this.formItem = this.getForm('uniFormsItem');
// this.formItem && this.formItem.setValue(this.value)
if (this.formItem) {
this.isTop = 6;
if (this.formItem.name) {
// 如果存在name添加默认值,否则formData 中不存在这个字段不校验
if(!this.is_reset){
this.is_reset = false;
this.formItem.setValue(this.dataValue);
}
this.rename = this.formItem.name;
this.form.inputChildrens.push(this);
}
}
if (this.localdata && this.localdata.length !== 0) {
this.isLocal = true;
this.range = this.localdata;
this.dataList = this.getDataList(this.getSelectedValue(this.range));
} else {
if (this.collection) {
this.isLocal = false;
this.loadData();
}
}
},
methods: {
loadData() {
this.mixinDatacomGet().then(res=>{
this.mixinDatacomResData = res.result.data;
if(this.mixinDatacomResData.length === 0){
this.isLocal = false;
this.mixinDatacomErrorMessage = this.emptyText;
}else{
this.isLocal = true;
}
}).catch(err=>{
this.mixinDatacomErrorMessage = err.message;
})
},
/**
* 获取父元素实例
*/
getForm(name = 'uniForms') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false;
parentName = parent.$options.name;
}
return parent;
},
chagne(e) {
const values = e.detail.value;
let detail = {
value: [],
data: []
};
if (this.multiple) {
this.range.forEach(item => {
if (values.includes(item[this.map.value] + '')) {
detail.value.push(item[this.map.value]);
detail.data.push(item);
}
})
} else {
const range = this.range.find(item => (item[this.map.value] + '') === values);
if (range) {
detail = {
value: range[this.map.value],
data: range
}
}
}
this.formItem && this.formItem.setValue(detail.value);
// TODO 兼容 vue2
this.$emit('input', detail.value);
// // TOTO 兼容 vue3
this.$emit('update:modelValue', detail.value);
this.$emit('change', {
detail
});
if (this.multiple) {
// 如果 v-model 没有绑定 ,则走内部逻辑
// if (this.value.length === 0) {
this.dataList = this.getDataList(detail.value, true)
// }
} else {
this.dataList = this.getDataList(detail.value)
}
},
/**
* 获取渲染的新数组
* @param {Object} value 选中内容
*/
getDataList(value) {
// 解除引用关系,破坏原引用关系,避免污染源数据
let dataList = JSON.parse(JSON.stringify(this.range));
let list = [];
if (this.multiple) {
if (!Array.isArray(value)) {
value = []
}
}
dataList.forEach((item, index) => {
item.disabled = item.disable || item.disabled || false;
if (this.multiple) {
if (value.length > 0) {
let have = value.find(val => val === item[this.map.value]);
item.selected = have !== undefined;
} else {
item.selected = false;
}
} else {
item.selected = value === item[this.map.value];
}
list.push(item)
});
return this.setRange(list)
},
/**
* 处理最大最小值
* @param {Object} list
*/
setRange(list) {
let selectList = list.filter(item => item.selected);
let min = Number(this.min) || 0;
let max = Number(this.max) || '';
list.forEach((item, index) => {
if (this.multiple) {
if (selectList.length <= min) {
let have = selectList.find(val => val[this.map.value] === item[this.map.value]);
if (have !== undefined) {
item.disabled = true
}
}
if (selectList.length >= max && max !== '') {
let have = selectList.find(val => val[this.map.value] === item[this.map.value]);
if (have === undefined) {
item.disabled = true
}
}
}
this.setStyles(item, index);
list[index] = item
});
return list
},
/**
* 设置 class
* @param {Object} item
* @param {Object} index
*/
setStyles(item, index) {
// 设置自定义样式
item.styleBackgroud = this.setStyleBackgroud(item);
item.styleIcon = this.setStyleIcon(item);
item.styleIconText = this.setStyleIconText(item);
item.styleRightIcon = this.setStyleRightIcon(item);
},
/**
* 获取选中值
* @param {Object} range
*/
getSelectedValue(range) {
if (!this.multiple) return this.dataValue;
let selectedArr = [];
range.forEach((item) => {
if (item.selected) {
selectedArr.push(item[this.map.value])
}
});
return this.dataValue && this.dataValue.length > 0 ? this.dataValue : selectedArr
},
/**
* 设置背景样式
*/
setStyleBackgroud(item) {
let styles = {}
let selectedColor = this.selectedColor?this.selectedColor:''
if (this.mode !== 'list') {
styles['border-color'] = item.selected?selectedColor:''
}
if (this.mode === 'tag') {
styles['background-color'] = item.selected? selectedColor:''
}
let classles = ''
for (let i in styles) {
classles += `${i}:${styles[i]};`
}
return classles
},
setStyleIcon(item) {
let styles = {}
let classles = ''
let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
styles['background-color'] = item.selected?selectedColor:'#fff'
styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
if(!item.selected && item.disabled){
styles['background-color'] = '#F2F6FC'
styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
}
for (let i in styles) {
classles += `${i}:${styles[i]};`
}
return classles
},
setStyleIconText(item) {
let styles = {}
let classles = ''
let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
if (this.mode === 'tag') {
styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:'#fff'):'#666'
} else {
styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:selectedColor):'#666'
}
if(!item.selected && item.disabled){
styles.color = '#999'
}
for (let i in styles) {
classles += `${i}:${styles[i]};`
}
return classles
},
setStyleRightIcon(item) {
let styles = {}
let classles = ''
if (this.mode === 'list') {
styles['border-color'] = item.selected?this.styles.selectedColor:'#DCDFE6'
}
for (let i in styles) {
classles += `${i}:${styles[i]};`
}
return classles
}
}
}
</script>
<style lang="scss">
$checked-color: $primary-color !important;
$border-color: #DCDFE6;
$disable:0.4;
@mixin flex {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
}
.uni-data-loading {
@include flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 0.36rem;
padding-left: 0.1rem;
color: #999;
}
.uni-data-checklist {
position: relative;
z-index: 0;
flex: 1;
// 多选样式
.checklist-group {
@include flex;
flex-direction: row;
flex-wrap: wrap;
&.is-list {
flex-direction: column;
}
.checklist-box {
@include flex;
flex-direction: row;
align-items: center;
position: relative;
margin: 0.05rem 0;
margin-right: 0.25rem;
.hidden {
position: absolute;
opacity: 0;
}
// 文字样式
.checklist-content {
@include flex;
flex: 1;
flex-direction: row;
align-items: center;
justify-content: space-between;
.checklist-text {
font-size: 0.14rem;
color: #666;
margin-left: 0.05rem;
line-height: 0.14rem;
}
.checkobx__list {
border-right-width: 0.01rem;
border-right-color: $primary-color;
border-right-style: solid;
border-bottom-width:0.01rem;
border-bottom-color: $primary-color;
border-bottom-style: solid;
height: 0.12rem;
width: 0.06rem;
left: -0.05rem;
transform-origin: center;
transform: rotate(45deg);
opacity: 0;
}
}
// 多选样式
.checkbox__inner {
/* #ifndef APP-NVUE */
flex-shrink: 0;
box-sizing: border-box;
/* #endif */
position: relative;
width: 0.16rem;
height: 0.16rem;
border: 0.01rem solid $border-color;
border-radius: 0.04rem;
background-color: #fff;
z-index: 1;
.checkbox__inner-icon {
position: absolute;
/* #ifdef APP-NVUE */
top: 0.02rem;
/* #endif */
/* #ifndef APP-NVUE */
top: 0.01rem;
/* #endif */
left: 0.05rem;
height: 0.08rem;
width: 0.04rem;
border-right-width: 0.01rem;
border-right-color: #fff;
border-right-style: solid;
border-bottom-width:0.01rem ;
border-bottom-color: #fff;
border-bottom-style: solid;
opacity: 0;
transform-origin: center;
transform: rotate(40deg);
}
}
// 单选样式
.radio__inner {
@include flex;
/* #ifndef APP-NVUE */
flex-shrink: 0;
box-sizing: border-box;
/* #endif */
justify-content: center;
align-items: center;
position: relative;
width: 0.16rem;
height: 0.16rem;
border: 0.01rem solid $border-color;
border-radius: 0.16rem;
background-color: #fff;
z-index: 1;
.radio__inner-icon {
width: 0.08rem;
height: 0.08rem;
border-radius: 0.1rem;
opacity: 0;
}
}
// 默认样式
&.is--default {
// 禁用
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
.checkbox__inner {
background-color: #F2F6FC;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.radio__inner {
background-color: #F2F6FC;
border-color: $border-color;
}
.checklist-text {
color: #999;
}
}
// 选中
&.is-checked {
.checkbox__inner {
border-color: $checked-color;
background-color: $checked-color;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
}
.radio__inner {
border-color: $checked-color;
.radio__inner-icon {
opacity: 1;
background-color: $checked-color;
}
}
.checklist-text {
color: $checked-color;
}
// 选中禁用
&.is-disable {
.checkbox__inner {
opacity: $disable;
}
.checklist-text {
opacity: $disable;
}
.radio__inner {
opacity: $disable;
}
}
}
}
// 按钮样式
&.is--button {
margin-right: 0.1rem;
padding: 0.05rem 0.1rem;
border: 0.01rem $border-color solid;
border-radius: 0.03rem;
transition: border-color 0.2s;
// 禁用
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
border: 0.01rem #eee solid;
opacity: $disable;
.checkbox__inner {
background-color: #F2F6FC;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.radio__inner {
background-color: #F2F6FC;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.checklist-text {
color: #999;
}
}
&.is-checked {
border-color: $checked-color;
.checkbox__inner {
border-color: $checked-color;
background-color: $checked-color;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
}
.radio__inner {
border-color: $checked-color;
.radio__inner-icon {
opacity: 1;
background-color: $checked-color;
}
}
.checklist-text {
color: $checked-color;
}
// 选中禁用
&.is-disable {
opacity: $disable;
}
}
}
// 标签样式
&.is--tag {
margin-right: 0.1rem;
padding: 0.05rem 0.1rem;
border: 0.01rem $border-color solid;
border-radius: 0.3rem;
background-color: #f5f5f5;
.checklist-text {
margin: 0;
color: #666;
}
// 禁用
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
opacity: $disable;
}
&.is-checked {
background-color: $checked-color;
border-color: $checked-color;
.checklist-text {
color: #fff;
}
}
}
// 列表样式
&.is--list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
padding: 0.1rem 0.15rem;
padding-left: 0;
margin: 0;
&.is-list-border {
border-top: 0.01rem #eee solid;
}
// 禁用
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
.checkbox__inner {
background-color: #F2F6FC;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.checklist-text {
color: #999;
}
}
&.is-checked {
.checkbox__inner {
border-color: $checked-color;
background-color: $checked-color;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
}
.radio__inner {
.radio__inner-icon {
opacity: 1;
}
}
.checklist-text {
color: $checked-color;
}
.checklist-content {
.checkobx__list {
opacity: 1;
border-color: $checked-color;
}
}
// 选中禁用
&.is-disable {
.checkbox__inner {
opacity: $disable;
}
.checklist-text {
opacity: $disable;
}
}
}
}
}
}
}
</style>

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