This commit is contained in:
xyc 2025-10-14 20:13:29 +08:00
parent da394abd70
commit 53fab43b7c
38 changed files with 6277 additions and 0 deletions

BIN
components/qianziyu-select/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,166 @@
<template>
<view>
<u-popup :show="show" @close="cancel">
<view class="title">{{popupTitle}}</view>
<view style="padding: 20rpx;">
<u-search v-if="showSearch" @custom="search" @search="search" :placeholder="placeholder"
v-model="keyword"></u-search>
<u-gap v-if="showSearch" height="15"></u-gap>
<scroll-view :scroll-top="scrollTop" scroll-y="true" class="scroll-Y" @scrolltolower="$emit('lower')">
<!--单选-->
<u-radio-group v-if="type == 'radio'" :borderBottom="true" iconPlacement="right" placement="column"
@change="groupChange" v-model="radioValue">
<u-radio :customStyle="{marginBottom: '12px'}" v-for="(item, index) in dataLists" :key="index"
:label="item[name]" :name="index">
</u-radio>
</u-radio-group>
<!--多选-->
<u-checkbox-group v-if="type == 'checkbox'" :borderBottom="true" placement="column"
iconPlacement="right" @change="checkboxChange" v-model="checkboxValue">
<u-checkbox :customStyle="{marginBottom: '12px',paddingBottom:'12px'}"
v-for="(item, index) in dataLists" :key="index" :label="item[name]" :name="index">
</u-checkbox>
</u-checkbox-group>
</scroll-view>
<u-gap height="45"></u-gap>
<view class="bottons">
<u-row>
<u-col customStyle="padding:0 10rpx 20rpx 20rpx" span="6">
<u-button @click="cancel">取消</u-button>
</u-col>
<u-col customStyle="padding:0 20rpx 20rpx 10rpx" span="6">
<u-button @click="submit" type="primary" throttleTime="1000" :disabled="(JSON.stringify(radioData) === '{}') && (checkboxData.length === 0)">确认</u-button>
</u-col>
</u-row>
</view>
</view>
</u-popup>
</view>
</template>
<script>
/**
* 公共选择下拉框基于uview支持下拉加载列表搜索单选多选
* @author qianziyu
* @description 弹出层选择器基于uview中u-popup实现
* @property {Array} dataLists 数据列表
* @property {String} name 列表显示的字段名
* @property {Boolean} show 是否展示弹窗 (默认 false )
* @property {String} type 选择类型 单选多选 (默认 单选 )
* @property {Boolean} showSearch 是否显示搜索框 (默认 true )
* @property {String} popupTitle 列表标题
* @property {String} placeholder 搜索框placeholder
* @event {Function} search 搜索事件返回keyword
* @event {Function} lower 滑动到底部触发用于下拉加载新数据
* @event {Function} cancel 组件关闭事件
* @event {Function} submit 提交按钮,返回选中的列表数据
* @example <common-select :show="show" :popupTitle="popupTitle" @cancel="show=false" @search="selectSearch" name="cworkStationName" @submit="onsubmit"
:dataLists="dataLists" placeholder="输入工站名称搜索"></common-select>
*/
export default {
name: "qianziyu-select",
props: {
dataLists: {
default: {},
type: Array
},
name: {
default: 'name',
},
show: {
default: false,
type: Boolean
},
type: {
default: 'radio',
type: String
},
showSearch: {
default: true,
type: Boolean
},
popupTitle: {
default: '列表选择',
type: String
},
placeholder: {
default: '请输入搜索内容'
}
},
data() {
return {
keyword: '',
scrollTop: 0,
checkboxData: [],
checkboxValue:[],
radioData: {},
radioValue: ''
};
},
methods: {
checkboxChange(n) {
this.checkboxData=[]
n.forEach(key=>{
this.checkboxData.push(this.dataLists[key])
})
},
//
groupChange(n) {
this.radioData = this.dataLists[n]
},
//
search() {
this.$emit('search', this.keyword)
},
//
cancel() {
this.$emit('cancel')
},
//
submit() {
if (this.type == 'radio') {
if (JSON.stringify(this.radioData) == '{}') {
uni.$u.toast('请选择数据')
return;
}
this.$emit('submit', this.radioData)
} else if (this.type == 'checkbox') {
if (this.checkboxData.length == 0) {
uni.$u.toast('请选择数据')
return;
}
this.$emit('submit', this.checkboxData)
}
}
}
}
</script>
<style lang="scss" scoped>
.u-popup {
.title {
border-bottom: 1px solid #f7f7f7;
padding: 20rpx;
text-align: center;
font-weight: bold;
}
}
.scroll-Y {
height: 650rpx;
}
.bottons {
background-color: white;
position: fixed;
left: 0;
bottom: 0;
right: 0;
bottom: constant(safe-area-inset-bottom);
bottom: env(safe-area-inset-bottom);
}
</style>

View File

@ -0,0 +1,442 @@
<template>
<view class="signature-wrap">
<view class="img-wrap" @tap="showSignature()" @touchstart="touchSignature()">
<image :src="absPrevView" mode="scaleToFill"></image>
</view>
<view v-if="!disabled" v-show="show" class="signature-contain">
<view class="signature-main" style="z-index: 3000;">
<view class="signature-title"><text v-for="t in titles">{{t}}</text></view>
<canvas disable-scroll="true" class="signature" :class="cid" canvas-id="cvs" @touchstart="touchstart"
@touchmove="touchmove" @touchend="touchend"></canvas>
<view class="signature-btns">
<view class="btn btn-cancel cu-btn bg-main margin-tb-sm text-white" @tap="cancelSignature()">
<text></text><text></text>
</view>
<view class="btn btn-clear cu-btn bg-main margin-tb-sm text-white" @tap="clearSignature();">
<text></text><text></text>
</view>
<view class="btn btn-ok cu-btn bg-main margin-tb-sm text-white" @tap="onOK()">
<text></text><text></text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
let _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var _utf8_encode = function(string) {
string = string.replace(/\r\n/g, "\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
let base64encode = function(input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output;
}
export default {
cxt: null,
data() {
return {
VERSION: '1.0.0',
cid: 'cvs',
show: false,
ctrl: null,
listeners: [],
prevView: '',
draws: [],
lines: [],
line: null,
};
},
props: {
value: {
default: '',
},
title: {
type: String,
default: '请签字',
},
disabled: {
type: Boolean,
default: false,
}
},
watch: {
value() {
this.prevView = this.value;
}
},
computed: {
titles() {
return this.title.split('')
},
absPrevView() {
var pv = this.prevView;
// if(pv){
// pv = this.$wrapUrl(pv)
// }
return pv;
}
},
mounted() {
this.prevView = this.value;
console.log('dx')
},
methods: {
onOK() {
let data = this.ctrl.getValue();
this.$emit('input', data);
this.prevView = data;
this.hideSignature();
let f = this.listeners.shift();
if (f) {
f(data);
}
},
touchSignature() {
let sig = this.prevView
if (!sig || !sig.length) {
this.showSignature()
}
},
showSignature() {
if (this.disabled)
return;
if (!this.ctrl) {
this.initCtrl();
} else if (!this.show) {
this.clearSignature();
this.show = true;
}
},
async getSyncSignature() {
this.showSignature();
return await new Promise(async (resolve, reject) => {
this.listeners.push((res) => {
resolve(res);
});
});
},
cancelSignature() {
this.listeners.map((f) => {
f(null);
})
this.hideSignature();
},
hideSignature() {
this.ctrl && this.ctrl.clear();
this.show = false;
},
clearSignature() {
this.ctrl && this.ctrl.clear();
},
async initCtrl() {
this.show = true;
let cxt = uni.createCanvasContext(this.cid, this);
this.cxt = cxt;
// cxt.clearRect(0,0,c.width,c.height);
this.ctrl = {
width: 0,
height: 0,
clear: () => {
this.lines = [];
let info = uni.createSelectorQuery().in(this).select("." + this.cid);
info.boundingClientRect((data) => {
if (data) {
cxt.clearRect(0, 0, data.width, data.height);
if (data.width && data.height) {
this.ctrl.width = data.width;
this.ctrl.height = data.height;
}
}
}).exec();
this.redraw();
},
getValue: () => {
if (!this.lines.length)
return '';
let svg = this._get_svg();
// new Buff
let b64 = base64encode(svg);
let data = 'data:image/svg+xml;base64,' + b64;
// console.log(svg);
// console.log(data);
return data;
},
};
this.$nextTick(function() {
this.ctrl.clear();
})
},
_get_svg() {
let r = -90;
let paths = [];
let raww = this.ctrl.width;
let rawh = this.ctrl.height;
let width = Math.abs(r) != 90 ? raww : rawh;
let height = Math.abs(r) == 90 ? raww : rawh;
let cx = raww / 2;
let cy = rawh / 2;
let PI = Math.PI;
let R = (r || 0) % 360;
let cosv = Math.cos(R * PI / 180);
let sinv = Math.sin(R * PI / 180);
let dcx = (width - raww) / 2;
let dcy = (height - rawh) / 2;
let trans = function(p) {
if (!R) {
return p;
} else {
let nx = (p.x - cx) * cosv - (p.y - cy) * sinv + cx;
let ny = (p.x - cx) * sinv + (p.y - cy) * cosv + cy;
return {
x: nx + dcx,
y: ny + dcy
};
}
return p;
}
this.lines.map(l => {
if (l.points.length < 2) {
return;
}
let sp = trans(l.start)
let pts = [`M ${sp.x} ${Number(sp.y)}`];
l.points.map(p => {
let np = trans(p)
pts.push(`L ${np.x} ${Number(np.y)}`);
});
paths.push(
`<path stroke-linejoin="round" stroke-linecap="round" stroke-width="3" stroke="rgb(0,0,0)" fill="none" d="${pts.join(' ')}"/>`
);
})
let svg =
`<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="${width}" height="${height}">${paths.join('\n')}</svg>`;
return svg;
},
_get_svg_raw() {
let paths = [];
this.lines.map(l => {
if (l.points.length < 2) {
return;
}
let pts = [`M ${l.start.x} ${Number(l.start.y)}`];
l.points.map(p => {
pts.push(`L ${p.x} ${Number(p.y)}`);
});
paths.push(
`<path stroke-linejoin="round" stroke-linecap="round" stroke-width="3" stroke="rgb(0,0,0)" fill="none" d="${pts.join(' ')}"/>`
);
})
let width = this.ctrl.width;
let height = this.ctrl.height;
let svg =
`<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="${width}" height="${height}" transform="rotate(-90)">${paths.join('\n')}</svg>`;
return svg;
},
_get_point(e) {
return {
x: e.changedTouches[0].x.toFixed(1),
y: e.changedTouches[0].y.toFixed(1),
}
},
touchstart(e) {
let p = this._get_point(e);
this.line = {
start: p,
points: [p],
}
this.lines.push(this.line);
},
touchmove(e) {
let p = this._get_point(e);
this.line.points.push(p)
if (!this.tm) {
this.tm = setTimeout(() => {
this.redraw();
this.tm = 0;
}, 10)
}
},
touchend(e) {
let p = this._get_point(e);
this.line.points.push(p)
this.line.end = p
this.redraw()
},
redraw() {
let cxt = this.cxt;
cxt.setStrokeStyle("#000");
cxt.setLineWidth(3);
var last = null;
this.lines.map(l => {
cxt.beginPath();
if (l.points.length < 2) {
return;
}
cxt.moveTo(l.start.x, l.start.y);
l.points.map(p => {
cxt.lineTo(p.x, p.y)
})
cxt.stroke()
})
cxt.draw()
},
canvasIdErrorCallback: function(e) {
console.error(e.detail.errMsg)
}
}
}
</script>
<style lang="scss">
.signature-wrap {
height: 100%;
width: 100%;
// padding: 0 5px;
// min-width: 60vw;
.img-wrap {
width: 100%;
min-height: 200rpx;
display: flex;
align-items: center;
text-align: center;
align-content: center;
justify-content: center;
image {
width: 100%;
}
// background: red;
}
}
.signature-contain {
z-index: 9000;
position: fixed;
left: 0;
top: 0;
width: 100%;
.signature-main {
background: white;
flex-direction: row-reverse;
display: flex;
align-items: stretch;
height: 101%;
overflow: scroll;
}
.signature-title {
font-weight: bold;
font-size: 18px;
display: flex;
padding: 0 20rpx;
flex-direction: column;
justify-content: center;
height: 100vh;
color: $uni-text-color;
text {
transform: rotate(90deg);
}
}
.signature {
border: 1px dotted black;
border-bottom: 1px dotted black;
background: #FFF;
margin: 10px 0;
width: 90vw;
height: 90vh;
align-self: center;
// pointer-events:none;
}
.signature-btns {
display: flex;
padding: 2px;
// margin-right: 5px;
flex-direction: column;
.btn {
flex-grow: 1;
flex-shrink: 0;
padding: 20rpx;
font-size: 20px;
margin: 0;
text-align: center;
text-decoration: none;
height: 30vh;
display: flex;
align-content: center;
justify-content: center;
flex-direction: column;
text {
transform: rotate(90deg);
}
&+.btn {
border-top: 1px solid #eee;
}
&.btn-clear {
// background-color: #fc2a07;
color: $uni-color-success;
}
&.btn-cancel {
// background-color: #eff4f4;
color: $uni-color-warning;
}
&.btn-ok {
// background-color: $uni-color-success;
color: $uni-color-primary;
}
}
}
}
</style>

View File

@ -0,0 +1,216 @@
<template>
<view class="container">
<!-- 顶部背景 -->
<view class="header">
<image class="bg-img" src="/static/images/table_header.png" mode="aspectFill"></image>
<view style="display: flex;align-items: center;padding-left: 20rpx;">
<uni-icons style="position: absolute;left: 30rpx;" @click="goBack" type="left" color="black"
size="24"></uni-icons>
</view>
<view class="title">{{title}}业务总览</view>
</view>
<!-- 表格 -->
<uni-table emptyText="暂无更多数据" style="margin-top: 30rpx;">
<!-- 表头行 -->
<uni-tr style="background: #E5EEFF;">
<uni-th>车辆信息</uni-th>
<uni-th>业务</uni-th>
<!-- <uni-th>渠道</uni-th> -->
<uni-th>来源</uni-th>
<uni-th>经办人</uni-th>
<uni-th>办理时间</uni-th>
<uni-th>付款状态</uni-th>
</uni-tr>
<!-- 表格数据行 -->
<uni-tr v-for="(item,index) in list" :key="index" class="table-row" @click.native="goWorkDetail(item)"
:class="{'row-bg': index % 2 === 1}">
<uni-td>{{ item.carNo }}</uni-td>
<uni-td>{{ item.sourceStr }}</uni-td>
<!-- <uni-td>{{ item.channel }}</uni-td> -->
<uni-td>{{ item.busiFrom }}</uni-td>
<uni-td>{{ item.handleName }}</uni-td>
<uni-td>{{ formatDateTimeToMinute(item.createTime) }}</uni-td>
<uni-td>{{ dictData[item.payStatus] }}</uni-td>
</uni-tr>
</uni-table>
<uni-pagination :current="current" :pageSize="queryParams.pageSize" :total="total"
@change="onPageChange"></uni-pagination>
</view>
</template>
<script>
import request from '../../utils/request';
import {
formatDateTimeToMinute,
getDictTextByCodeAndValue,
getDictByCode
} from '@/utils/utils.js'
export default {
data() {
return {
list: [],
data: undefined,
queryParams: {
pageSize: 15,
pageNo: 1
},
title: '',
total: 0,
current: 0,
safeLeft: 0,
dictData: undefined,
}
},
methods: {
formatDateTimeToMinute,
getDictTextByCodeAndValue,
goBack() {
uni.redirectTo({
url: '/pages/white/white'
});
},
async getDict() {
const list = await getDictByCode('repair_pay_status')
this.dictData = list?.reduce((map, item) => {
map[item.value] = item.label
return map
}, {}) ?? {} // list reduce undefined使
console.log('dict', this.dictData)
},
getList() {
request({
url: `/admin-api/repair/statistics/pageByCustomerOrCar`,
params: this.queryParams
}).then(res => {
this.list = res.data.records
this.total = res.data.total
this.current = res.data.current
})
},
onPageChange(e) {
this.queryParams.pageNo = e.current
this.getList()
},
goWorkDetail(data) {
console.log('执行');
// 退
// #ifdef APP-PLUS
plus.screen.lockOrientation('portrait-primary'); //
plus.navigator.setFullscreen(false);
// #endif
const innerUrl = `/pages-order/orderDetail/orderDetail?id=${data.id}&isDetail=1`
// const url = `/pages-business/white/newWhite?url=${encodeURIComponent(innerUrl)}`
setTimeout(() => {
uni.navigateTo({
url: innerUrl
})
}, 1000); // 300
}
},
onShow() {},
//
onLoad(options) {
this.getDict()
console.log('传进来的数据', options);
if (options.data) {
const queryParams = JSON.parse(decodeURIComponent(options.data));
this.data = queryParams
if (options.type == 'customer') {
this.queryParams.phone = queryParams.customerPhone
this.title = queryParams.customerName
} else {
this.queryParams.carNo = queryParams.carNum
this.title = queryParams.carNum
}
this.getList()
}
},
//
onBackPress() {
console.log('执行跳转');
//
uni.redirectTo({
url: '/pages/white/white'
});
return true;
},
}
</script>
<style scoped>
.container {
width: 100%;
height: 100%;
background-color: white;
padding-left: env(safe-area-inset-left);
}
/* 顶部背景和标题 */
.header {
display: flex;
position: relative;
height: 100rpx;
justify-content: center;
align-items: center;
padding: 20rpx 40rpx;
padding-top: var(--status-bar-height); //
}
.bg-img {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
/* z-index: -1; */
}
.title {
text-align: center;
font-size: 36rpx;
font-weight: bold;
color: #333;
line-height: 100rpx;
z-index: 2;
}
/* 表格 */
.table {
margin: 20rpx;
border-radius: 12rpx;
overflow: hidden;
}
.table-row {
/* flex-direction: row;
display: flex; */
}
.table-header {
background-color: #f2f6ff;
font-weight: bold;
}
.table-cell {
flex: 1;
padding: 20rpx;
text-align: center;
font-size: 28rpx;
color: #555;
}
.row-bg {
background-color: #f9fbff;
}
.status {
color: #d4a017;
/* 金色 */
font-weight: 500;
}
</style>

View File

@ -0,0 +1,745 @@
<template>
<view class="business-management">
<!-- 业务总览头部 -->
<view class="header" style="position: relative;">
<view style="display: flex;">
<uni-icons style="position: absolute;left: 0;" @click="goBack" type="left" color="white"
size="24"></uni-icons>
<text class="title">业务管理</text>
</view>
<view class="subtitle">
<view class="text_">
<image style="height: 40rpx;width: 8rpx;" src="/static/images/business/text_left.png"></image>
<view>
业务总览
</view>
</view>
<view>
<uni-datetime-picker v-model="queryParams.dateRange" type="daterange" @change="initData()"
rangeSeparator="至" class="">
<view class="cont_time">
<view class="cont_size">{{queryParams.dateRange[0]}}</view>
<view class="bule_size">~</view>
<view class="cont_size">{{queryParams.dateRange[1]}}</view>
<view class=""> <u-icon name="arrow-down-fill" color="#0357FF" size="12"></u-icon></view>
</view>
</uni-datetime-picker>
</view>
</view>
<!-- 数据卡片 -->
<view class="data-cards">
<!-- 选项卡 -->
<view class="tabs">
<view class="tab" :class="{'active':tapIndex == index}" v-for="(item,index) in tapList" :key="index"
@click="tapIcon(index)">
<view class="">{{item.label || "无"}}</view>
<view class="tap_img" style="display: block;width: 30rpx;" v-show="tapIndex == index">
<image src="/static/images/icon_tap.png" mode="widthFix" class="tap_icon"></image>
</view>
</view>
</view>
<view class="card-row-1">
<view class="data-card">
<text class="card-value">进厂数</text>
<text class="card-label">{{ bossNum.newOrderNum }}</text>
</view>
<view class="data-card">
<text class="card-value">维修中</text>
<text class="card-label">{{ bossNum.workingNum }}</text>
</view>
<view class="data-card">
<text class="card-value">已竣工</text>
<text class="card-label">{{ bossNum.overNum }}</text>
</view>
</view>
<view class="card-row-2">
<view class="data-card">
<text class="card-value">已交车</text>
<text class="card-label">{{ bossNum.giveCusNum }}</text>
</view>
<view class="data-card">
<text class="card-value">未结算</text>
<text class="card-label">{{ bossNum.noPayNum }}</text>
</view>
<view class="data-card">
<text class="card-value">在厂数</text>
<text class="card-label">{{ bossNum.inCompanyNum }}</text>
</view>
</view>
</view>
<view class="business_title">
<!-- 选项卡 -->
<view class="tabs">
<view class="tab" :class="{'active':busTapIndex == index}" v-for="(item,index) in busTapList"
:key="index" @click="tapBusIcon(index)">
<view class="">{{item.label || "无"}}</view>
<view class="tap_img" style="display: block;" v-show="busTapIndex == index">
<image src="/static/images/icon_tap.png" mode="widthFix" class="tap_icon"></image>
</view>
</view>
</view>
<view class="">
<uni-search-bar radius="50" bgColor="#F8F4FF" @confirm="onRefresherrefresh"
v-model="queryParams.search" @cancel="onRefresherrefresh" @clear="onRefresherrefresh"
placeholder="请输入搜索内容"></uni-search-bar>
</view>
</view>
</view>
<view class="list">
<!-- 客户列表 -->
<scroll-view class="client-list" scroll-y style="height:100%" @scrolltolower="onReachBottomCus"
refresher-enabled @refresherrefresh="onRefresherrefresh" :refresher-triggered="isTriggered">
<view class="client-item" v-for="(item,index) in busList" :key="index" @click="goDetail(item)">
<view class="last-time">
<view class="">
最近办理业务时间{{item.bizTime}}
</view>
<view class="">
次数{{item.consumeCount}}
</view>
</view>
<view class="card_bottom">
<view class="info_left">
<view class="user_">
<view class="client-name" v-if="busTapIndex == 0">{{item.customerName}}</view>
<view class="client-name" v-else>{{item.carNum}}</view>
<view class="agent" v-if="busTapIndex == 0">经办人:{{item.handleName}}</view>
<view class="agent" v-else>{{item.customerName}}</view>
</view>
<view class="phone">联系电话{{item.customerPhone}}</view>
</view>
<view class="info_right">
<view class="top_text">{{item.sourceStr}}</view>
<view class="bottom_text">
最近办理业务
</view>
</view>
</view>
</view>
<view style="text-align: center" v-if="busList.length==0">
<image class="" src="@/static/images/nothing.png"></image>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import request from '../../utils/request'
import {
getDateRange
} from '../../utils/utils'
export default {
data() {
return {
searchKeyword: '',
//
isTriggered: true,
tapList: [{
label: "本日",
value: "day",
},
{
label: "本月",
value: "month",
},
{
label: "全部",
value: "all",
},
],
busTapList: [{
label: "客户",
value: "customer",
},
{
label: "车辆",
value: "car",
}
],
tapIndex: 0,
busTapIndex: 0,
queryParams: {
dateRange: [],
type: 'customer',
selectType: 'today',
pageNo: 1,
pageSize: 10,
search: null
},
bossNum: {},
busList: [],
}
},
async onLoad() {
await this.setCurrentMonthRange()
await this.initData()
},
methods: {
goBack() {
uni.navigateBack()
},
async tapIcon(index) {
this.tapIndex = index
await this.slectRange(index)
await this.initData()
},
tapBusIcon(index) {
this.busTapIndex = index
this.queryParams.type = this.busTapList[index].value
this.queryParams.pageNo = 1
this.busList = []
this.getList()
},
/**
* 初始化数据
*/
initData() {
this.getNum()
this.onRefresherrefresh()
},
getNum() {
request({
url: '/admin-api/repair/tickets/getBossNumNew',
method: 'get',
params: this.queryParams
}).then((res) => {
console.log(res)
if (res.code == 200) {
this.bossNum = res.data
}
})
},
getList() {
request({
url: '/admin-api/repair/statistics/listBusiness',
method: 'get',
params: this.queryParams
}).then((res) => {
if (res.code == 200) {
if (this.queryParams.pageNo == 1) {
this.busList = res.data.records
} else {
this.busList.push(...res.data.records)
}
}
//
this.total = res.data.total
this.isTriggered = false
})
},
clear() {},
cancel() {},
//
setCurrentMonthRange() {
// 使 Date
const now = new Date();
// Date
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
this.queryParams.dateRange = [
this.formatDate(now), //
this.formatDate(now), //
];
},
formatDate(d) {
// d Date
if (!(d instanceof Date)) {
console.error("formatDate() 期望接收 Date 对象,但收到:", d);
d = new Date(d); // Date
if (isNaN(d.getTime())) {
//
d = new Date(); // 使
}
}
return `${d.getFullYear()}-${this.pad(d.getMonth() + 1)}-${this.pad(
d.getDate()
)}`;
},
// 1 01
pad(n) {
return n.toString().padStart(2, "0");
},
slectRange(index) {
this.selected = index;
console.log(index, this.selected);
const {
value
} = this.tapList[index];
console.log(value);
this.queryParams.dateRange = getDateRange(value);
},
/**
* 上滑加载数据
*/
onReachBottomCus() {
// *
if (this.queryParams.pageNo * this.queryParams.pageSize >= this.total) {
uni.$u.toast('没有更多数据了')
return
}
//+1,
this.queryParams.pageNo++
//
this.getList()
},
/**
* 下拉刷新数据
*/
async onRefresherrefresh() {
this.isTriggered = true
this.pageNo = 1
this.total = 0
this.busList = []
await this.getList()
},
goDetail(data) {
uni.navigateTo({
url: `/pages-business/businessInfo/businessInfo?type=${this.queryParams.type}&data=${encodeURIComponent(JSON.stringify(data))}`
})
},
}
}
</script>
<style>
/* 全局样式 */
page {
background-color: #f5f5f5;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, sans-serif;
}
.business-management {
/* padding: 20rpx; */
background: #F2F5FE;
min-height: 100vh;
}
/* 头部样式 */
.header {
margin-bottom: 30rpx;
background: linear-gradient(180deg, #1A53FF 0%, #F2F5FE 100%);
/* height: 672rpx; */
color: white;
display: flex;
flex-direction: column;
align-items: center;
padding: 0 30rpx;
padding-top: calc(var(--status-bar-height) + 20rpx);
}
.title {
font-size: 36rpx;
font-weight: bold;
display: block;
margin-bottom: 10rpx;
}
.subtitle {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
margin-top: 20rpx;
.text_ {
display: flex;
view {
width: 112rpx;
height: 28rpx;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 800;
font-size: 28rpx;
color: #FFFFFF;
line-height: 42rpx;
text-align: left;
font-style: normal;
}
image {
margin-right: 20rpx;
}
}
}
.date-range {
width: 386rpx;
height: 56rpx;
background: #FFFFFF;
border-radius: 36rpx;
text-align: center;
.header {
width: 124rpx;
height: 24rpx;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: 24rpx;
color: #686C7A;
line-height: 36rpx;
text-align: right;
font-style: normal;
margin: auto 0;
}
}
/* 选项卡样式 */
.tabs {
display: flex;
margin-bottom: 30rpx;
padding-bottom: 20rpx;
}
.tab {
margin-right: 40rpx;
font-size: 28rpx;
color: #666;
padding: 10rpx 0;
width: 56rpx;
height: 28rpx;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: 28rpx;
color: #707677;
line-height: 42rpx;
text-align: right;
font-style: normal;
}
.tab.active {
color: #007AFF;
font-weight: bold;
width: 56rpx;
height: 28rpx;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: bold;
font-size: 28rpx;
color: #292D2E;
line-height: 42rpx;
text-align: right;
font-style: normal;
margin-bottom: 20rpx;
}
/* 数据卡片样式 */
.data-cards {
margin: 20rpx 0;
background: linear-gradient(180deg, #E4EFFF 0%, #FFFFFF 100%);
box-shadow: 0rpx 4rpx 16rpx 0rpx rgba(167, 179, 229, 0.25);
border-radius: 8rpx;
width: 726rpx;
/* height: 366rpx; */
padding: 20rpx 10rpx;
}
.card-row-1 {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
.data-card {
height: 112rpx;
width: 226rpx;
background: linear-gradient(180deg, #D3EFFF 0%, #E3F3FF 100%);
border-radius: 8rpx;
.card-value {
width: 84rpx;
height: 28rpx;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: 28rpx;
color: #14557E;
line-height: 42rpx;
text-align: center;
font-style: normal;
}
.card-label {
width: 52rpx;
height: 28rpx;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: bold;
font-size: 28rpx;
color: #3E4045;
line-height: 42rpx;
text-align: center;
font-style: normal;
}
}
}
.card-row-2 {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
.data-card {
height: 112rpx;
width: 226rpx;
background: linear-gradient(180deg, #D8E6FF 0%, #EDF6FF 100%);
border-radius: 8rpx;
}
.card-value {
width: 84rpx;
height: 28rpx;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: 28rpx;
color: #14557E;
line-height: 42rpx;
text-align: center;
font-style: normal;
}
.card-label {
width: 52rpx;
height: 28rpx;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: bold;
font-size: 28rpx;
color: #3E4045;
line-height: 42rpx;
text-align: center;
font-style: normal;
}
}
.data-card {
flex: 1;
background-color: #fff;
border-radius: 12rpx;
padding: 30rpx 20rpx;
margin: 0 10rpx;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.card-value {
font-size: 40rpx;
font-weight: bold;
margin-bottom: 10rpx;
}
.card-label {
font-size: 24rpx;
color: #666;
}
/* 客户区域样式 */
.client-section {
background-color: #fff;
border-radius: 12rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
}
.section-subtitle {
font-size: 24rpx;
color: #999;
}
.search-box {
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 10rpx;
padding: 20rpx;
}
.search-input {
flex: 1;
font-size: 28rpx;
margin-left: 15rpx;
}
.placeholder {
color: #999;
}
/* 客户列表样式 */
.client-list {
max-height: 60vh;
width: 100%;
}
.client-item {
background: #FFFFFF;
border-radius: 8rpx;
padding: 20rpx 15rpx;
margin-bottom: 30rpx
}
.client-name {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 500;
font-size: 28rpx;
text-align: left;
font-style: normal;
margin-right: 20rpx;
}
.agent {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: 24rpx;
color: #0357FF;
line-height: 36rpx;
text-align: left;
font-style: normal;
background-color: #DCE7FF;
border-radius: 20rpx;
padding: 0 10rpx;
}
.phone {
width: 312rpx;
height: 28rpx;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: 28rpx;
color: #8F9197;
line-height: 42rpx;
text-align: left;
font-style: normal;
}
/*
.last-time {
font-size: 26rpx;
color: #666;
display: block;
margin-bottom: 10rpx;
} */
.last-time {
border-bottom: 1px dashed #000;
padding-bottom: 50rpx;
width: 100%;
height: 24rpx;
font-weight: 400;
font-size: 24rpx;
color: #7E8390;
line-height: 36rpx;
text-align: left;
font-style: normal;
display: flex;
justify-content: space-between;
}
.tap_img {
height: 6rpx;
text-align: center;
margin: 0 auto;
image {
width: 34rpx;
height: 12rpx;
}
}
.cont_time {
background: #F1F4F7;
border-radius: 36rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0px 30rpx;
width: 386rpx;
height: 56rpx;
background: #FFFFFF;
border-radius: 36rpx;
}
.cont_size {
font-weight: 400;
font-size: 24rpx;
color: #686C7A;
}
.business_title {
display: flex;
justify-content: space-between;
width: 100%;
height: 100rpx;
}
.user_ {
display: flex;
}
.card_bottom {
display: flex;
justify-content: space-between;
padding: 20rpx 0;
text-align: center;
}
.info_right {
text-align: right;
color: black;
.top_text {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 500;
font-size: 28rpx;
color: #323439;
line-height: 42rpx;
}
.bottom_text {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: 28rpx;
color: #686B71;
}
}
.list {
padding: 20rpx;
height: calc(100vh - 800rpx);
}
.tap_icon {
width: 40rpx;
height: 40rpx;
}
</style>

View File

@ -0,0 +1,896 @@
<template>
<view class="container">
<!-- 顶部背景 -->
<view class="header">
<image class="bg-img" src="/static/images/table_header.png" mode="aspectFill"></image>
<view style="display: flex;align-items: center;padding-left: 20rpx;">
<uni-icons style="position: absolute;left: 30rpx;" @click="goBack" type="left" color="black"
size="24"></uni-icons>
</view>
<view class="title">数据统计({{total}})</view>
</view>
<view class="content_top">
<!-- 选项卡 -->
<view class="tabs">
<view class="tab" :class="{'active':tapIndex == index}" v-for="(item,index) in tapList" :key="index"
@click="tapIcon(index)">
<view class="">{{item.label || "无"}}</view>
<view class="tap_img" style="display: block;" v-show="tapIndex == index">
<image src="/static/images/icon_tap.png" mode="widthFix" class="tap_icon"></image>
</view>
</view>
</view>
<view style="display: flex;">
<view class="search">
<picker @change="bindTimeTypeChange" range-key="label" :value="timeTypeIndex"
:range="timeTypeArray">
<view class="uni-input border_" style="padding: 5rpx 20rpx;display: flex;">
{{timeTypeArray[timeTypeIndex].label}}
<view class=""> <u-icon name="arrow-down-fill" color="#0357FF" size="12"></u-icon></view>
</view>
</picker>
</view>
<view style="align-items: center;display: flex; border-radius: 50rpx;">
<uni-datetime-picker v-model="queryParams.searchTimeArray" type="daterange"
@change="queryParams.pageNo=1;initData()" rangeSeparator="至" class="">
<view class="cont_time" style="border: 1px solid #0357FF;"
v-if="queryParams.searchTimeArray.length>0">
<view class="cont_size">{{queryParams.searchTimeArray[0]}}</view>
<view class="bule_size">~</view>
<view class="cont_size">{{queryParams.searchTimeArray[1]}}</view>
<view class=""> <u-icon name="arrow-down-fill" color="#0357FF" size="12"></u-icon></view>
</view>
<view v-else class="cont_time border_">
全部
<view class=""> <u-icon name="arrow-down-fill" color="#0357FF" size="12"></u-icon></view>
</view>
</uni-datetime-picker>
</view>
<view class="search" @click="screening">
筛选
</view>
</view>
</view>
<!-- 表格 -->
<view class="" style="padding-left: 30rpx;">
<view class="census" v-if="checkPermi(['repair:tick:profit'])">
<span class="credited">产值{{ laborPartsMoney }}</span>
<span class="notCredited">毛利&nbsp;:&nbsp;{{ totalProfit }}</span>
<span
class="onlinePay">含工时毛利率&nbsp;:&nbsp;{{ statisticsInfo && statisticsInfo.profitRateWithLabor !== null && statisticsInfo.profitRateWithLabor !== undefined && !isNaN(statisticsInfo.profitRateWithLabor) ? (statisticsInfo.profitRateWithLabor * 100).toFixed(2) : '0.00' }}
%</span>
<span
class="cashPay">不含工时毛利率&nbsp;:&nbsp;{{ statisticsInfo && statisticsInfo.profitRateWithoutLabor !== null && statisticsInfo.profitRateWithoutLabor !== undefined && !isNaN(statisticsInfo.profitRateWithoutLabor) ? (statisticsInfo.profitRateWithoutLabor * 100).toFixed(2) : '0.00' }}
%</span>
</view>
<uni-table emptyText="暂无更多数据">
<!-- 表头行 -->
<uni-tr style="background: #E5EEFF;">
<uni-th>车辆信息</uni-th>
<!-- <uni-th>业务</uni-th> -->
<!-- <uni-th>渠道</uni-th> -->
<uni-th>经办人</uni-th>
<uni-th>经办人手机号</uni-th>
<uni-th>进场时间</uni-th>
<uni-th>付款状态</uni-th>
<uni-th>结算时间</uni-th>
</uni-tr>
<!-- 表格数据行 -->
<uni-tr v-for="(item,index) in list" :key="index" class="table-row" @click.native="goWorkDetail(item)"
:class="{'row-bg': index % 2 === 1}">
<uni-td>{{ item.carNo }}</uni-td>
<!-- <uni-td>{{ item.sourceStr }}</uni-td> -->
<!-- <uni-td>{{ item.channel }}</uni-td> -->
<uni-td>{{ item.handleName }}</uni-td>
<uni-td>{{ item.handleMobile }}</uni-td>
<uni-td>{{ formatDateTimeToMinute(item.createTime) }}</uni-td>
<uni-td>{{ dictData[item.payStatus] }}</uni-td>
<uni-td>{{ item.settlementTime ? formatDateTimeToMinute(item.settlementTime) : '' }}</uni-td>
</uni-tr>
</uni-table>
<uni-pagination :current="current" :pageSize="queryParams.pageSize" :total="total"
@change="onPageChange"></uni-pagination>
</view>
<!-- 筛选弹窗 -->
<uni-popup ref="popup" type="right" background-color="#fff">
<view class="popup">
<!-- 添加滚动区域 -->
<scroll-view class="popup-content" scroll-y="true">
<view class="reset" @click="resetQueryParams">
<uni-icons type="loop" color="#165DFF"></uni-icons>
重置筛选
</view>
<view>
<uni-search-bar radius="50" bgColor="#F8F4FF" @confirm="queryParams.pageNo=1;initData()()"
v-model="queryParams.ticketNo" @cancel="initData()" @clear="initData()"
placeholder="请输入关键字"></uni-search-bar>
</view>
<uni-forms labelPosition="top" class="body-top-tab">
<uni-forms-item label="业务渠道">
<picker @change="bindBusiChange" :value="busiIndex" :range="busiList">
<view class="uni-input">{{busiList[busiIndex]}}</view>
</picker>
</uni-forms-item>
<uni-forms-item label="维修类别">
<picker @change="bindRepairTypeChange" :value="repairTypeIndex" :range="repairTypeList">
<view class="uni-input">{{repairTypeList[repairTypeIndex]}}</view>
</picker>
</uni-forms-item>
<uni-forms-item label="工种类型">
<picker @change="bindWorkTypeChange" :value="workTypeIndex" :range="workeTypeList">
<view class="uni-input">{{workeTypeList[workTypeIndex]}}</view>
</picker>
</uni-forms-item>
<uni-forms-item label="工单状态">
<picker @change="bindTicketsStatusChange" :value="ticketsStatusIndex"
:range="ticketsStatusList">
<view class="uni-input">{{ticketsStatusList[ticketsStatusIndex]}}</view>
</picker>
</uni-forms-item>
<uni-forms-item label="来源">
<uni-data-picker :localdata="cusFromList" v-model='queryParams.cusFrom'
popup-title="请选择渠道和来源" :map="{text:'name',value:'name'}"
@change="bindCusFromChange"></uni-data-picker>
</uni-forms-item>
<uni-forms-item label="收款状态">
<picker @change="bindpayStatusChange" :value="payStatusIndex" :range="payStatusList">
<view class="uni-input">{{payStatusList[payStatusIndex]}}</view>
</picker>
</uni-forms-item>
</uni-forms>
</scroll-view>
</view>
</uni-popup>
</view>
</template>
<script>
import request from '../../utils/request';
import {
formatDateTimeToMinute,
getDictTextByCodeAndValue,
getDictByCode,
getDateRange,
toPickerData,
handleTree
} from '@/utils/utils.js';
import {
checkPermi,
checkRole
} from "@/utils/permission"; //
import {
getToken,
getUserInfo,
getStrData,
getTenantId,
setJSONData,
getJSONData,
getStorageWithExpiry,
setStorageWithExpiry
} from '@/utils/auth'
export default {
data() {
return {
list: [],
data: undefined,
queryParams: {
searchTimeArray: [],
selectType: 'all',
pageNo: 1,
pageSize: 10,
search: null
},
title: '',
timeTypeArray: [{
label: '进场时间',
value: 'create'
}, {
label: '结算时间',
value: 'settlement'
}, ],
timeTypeIndex: 1,
total: 0,
current: 0,
//
workTypeIndex: 0,
//
repairTypeIndex: 0,
//
repairTypeList: ['按维修类别'],
//--
repairTypeValueList: [''],
//
workeTypeList: ['按工种类型'],
//--
workeTypeValueList: [''],
//
cusFromList: [
['按客户来源'],
],
//---
cusFromValueList: [''],
//
cusFromIndex: [0, 0],
//
ticketsStatusList: ['按工单状态', '进厂', '维修中', '已竣工', '已交车', '未结算', '在厂'],
//--
ticketsStatusValueList: ['', 'jinchang', 'weixiuzhong', 'yijungong', 'yijiaoche', 'weijiesuan',
'zaichang'
],
//
ticketsStatusIndex: 0,
//
payStatusList: ['按支付状态', '应收款', '已收款', '待收款'],
//--
payStatusValueList: ['', 'receivable', 'receivedAmount', 'pendingAmount'],
//
payStatusIndex: 0,
//
busiList: ['按业务渠道'],
//--
busiValueList: [''],
//
busiIndex: 0,
safeLeft: 0,
dictData: undefined,
statisticsInfo: {},
tapIndex: 0,
tapList: [{
label: "本日",
value: "day",
},
{
label: "本月",
value: "month",
},
{
label: "全部",
value: "all",
},
],
}
},
computed: {
laborPartsMoney() {
return this.statisticsInfo && this.statisticsInfo.totalLaborPartsMoney ? this.statisticsInfo
.totalLaborPartsMoney : 0
},
totalProfit() {
return this.statisticsInfo && this.statisticsInfo.totalProfit ? this.statisticsInfo.totalProfit : 0
},
profitRateWithLabor() {
if (this.statisticsInfo && this.statisticsInfo.profitRateWithLabor != null && !isNaN(this.statisticsInfo
.profitRateWithLabor)) {
return (this.statisticsInfo.profitRateWithLabor * 100).toFixed(2)
}
return '0.00'
},
profitRateWithoutLabor() {
if (this.statisticsInfo && this.statisticsInfo.profitRateWithoutLabor != null && !isNaN(this.statisticsInfo
.profitRateWithoutLabor)) {
return (this.statisticsInfo.profitRateWithoutLabor * 100).toFixed(2)
}
return '0.00'
}
},
watch: {
repairTypeIndex(newval) {
this.initData()
},
workTypeIndex(newval) {
this.initData()
},
ticketsStatusIndex(newval) {
this.initData()
},
workTypeIndex(newval) {
this.initData()
},
busiIndex(newval) {
this.initData()
},
payStatusIndex(newval) {
this.initData()
},
},
onResize() {
console.log('执行了刷新');
},
methods: {
checkPermi,
checkRole,
formatDateTimeToMinute,
getDictTextByCodeAndValue,
goBack() {
// setTimeout(() => {
uni.redirectTo({
url: '/pages/white/white'
});
// }, 300)
},
async initData() {
await this.getStatistics();
this.queryParams.pageNo = 1;
await this.getList()
},
setDictItem(dictCode, item) {
if ("repair_type" == dictCode) {
//
this.repairTypeList.push(item.label)
this.repairTypeValueList.push(item.value)
} else if ("cus_data_from" == dictCode) {
//
this.cusFromList.push(item.label)
this.cusFromValueList.push(item.value)
} else if ("repair_tickets_status" == dictCode) {
//
this.ticketsStatusList.push(item.label)
this.ticketsStatusValueList.push(item.value)
} else if ("repair_work_type" == dictCode) {
//
this.workeTypeList.push(item.label)
this.workeTypeValueList.push(item.value)
}
},
screening() {
this.$refs.popup.open()
},
/**
* 查2个数据字典备用---客户注册方式-cus_data_from维修业务分类-repair_type
*/
initDict(dictCode) {
let dictArray = getStorageWithExpiry(dictCode);
if (null == dictArray || undefined == dictArray) {
request({
url: '/admin-api/system/dict-data/type',
method: 'get',
params: {
type: dictCode
}
}).then((res) => {
if (res.code == 200) {
setStorageWithExpiry(dictCode, res.data, 3600)
this.$nextTick(() => {
res.data.map(item => {
this.setDictItem(dictCode, item)
})
})
}
})
} else {
this.$nextTick(() => {
dictArray.map(item => {
this.setDictItem(dictCode, item)
})
})
}
},
/**
* 切换维修项目类型
*/
bindRepairTypeChange(e) {
this.repairTypeIndex = e.detail.value
// this.onRefresherrefresh()
},
/**
* 切换维修项目类型
*/
bindBusiChange(e) {
this.busiIndex = e.detail.value
console.log('选择的渠道', this.busiValueList[e.detail.value]);
// this.onRefresherrefresh()
},
/**
* 切换客户来源
*/
bindCusFromChange(e) {
this.initData()
// this.onRefresherrefresh()
},
/**
* 切换工单状态
*/
bindTicketsStatusChange(e) {
this.ticketsStatusIndex = e.detail.value
// this.onRefresherrefresh()
},
/**
* 切换工单状态
*/
bindpayStatusChange(e) {
this.payStatusIndex = e.detail.value
// this.onRefresherrefresh()
},
/**
* 切换工种类型
*/
bindWorkTypeChange(e) {
this.workTypeIndex = e.detail.value
// this.onRefresherrefresh()
},
bindTimeTypeChange(e) {
console.log(e);
this.timeTypeIndex = e.detail.value
this.queryParams.timeType = this.timeTypeArray[e.detail.value].value
this.initData()
// this.onRefresherrefresh()
},
async getDict() {
const list = await getDictByCode('repair_pay_status')
this.dictData = list?.reduce((map, item) => {
map[item.value] = item.label
return map
}, {}) ?? {} // list reduce undefined使
console.log('dict', this.dictData)
},
slectRange(index) {
this.selected = index;
console.log(index, this.selected);
const {
value
} = this.tapList[index];
console.log(value);
this.queryParams.searchTimeArray = getDateRange(value);
},
async tapIcon(index) {
this.tapIndex = index
await this.slectRange(index)
this.queryParams.pageNo = 1
await this.initData()
},
setCurrentMonthRange() {
// 使 Date
const now = new Date();
// Date
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
this.queryParams.searchTimeArray = [
this.formatDate(now), //
this.formatDate(now), //
];
},
/**
* 获取业务来源和渠道
*/
queryBusiAndCus() {
request({
url: `/admin-api/business/list`,
method: 'GET',
params: {
systemCode: 'repair'
}
}).then(res => {
//pid0
this.busiList.push(
...res.data
.filter(item => item.pid === 0)
.map(item => item.name)
);
//pid0
this.busiValueList.push(
...res.data
.filter(item => item.pid === 0)
.map(item => item.name)
);
this.cusFromList = handleTree(res.data, 'id', 'pid')
})
},
formatDate(d) {
// d Date
if (!(d instanceof Date)) {
console.error("formatDate() 期望接收 Date 对象,但收到:", d);
d = new Date(d); // Date
if (isNaN(d.getTime())) {
//
d = new Date(); // 使
}
}
return `${d.getFullYear()}-${this.pad(d.getMonth() + 1)}-${this.pad(
d.getDate()
)}`;
},
// 1 01
pad(n) {
return n.toString().padStart(2, "0");
},
getList() {
let paramsObj = {
repairType: this.repairTypeValueList[this.repairTypeIndex],
workType: this.workeTypeValueList[this.workTypeIndex],
ticketsStatus: this.ticketsStatusValueList[this.ticketsStatusIndex],
busiFrom: this.busiValueList[this.busiIndex],
payStatus: this.payStatusValueList[this.payStatusIndex],
// timeType: 'settlement',
...this.queryParams
}
request({
url: `/admin-api/repair/tickets/pageType`,
params: paramsObj
}).then(res => {
this.list = res.data.records
this.total = res.data.total
this.current = res.data.current
})
},
onPageChange(e) {
this.queryParams.pageNo = e.current
this.getList()
},
goWorkDetail(data) {
console.log('执行');
// 退
// #ifdef APP-PLUS
plus.screen.lockOrientation('portrait-primary'); //
plus.navigator.setFullscreen(false);
// #endif
const innerUrl = `/pages-order/orderDetail/orderDetail?id=${data.id}&isDetail=1`
const url = `/pages-business/white/newWhite?url=${encodeURIComponent(innerUrl)}`
setTimeout(() => {
uni.redirectTo({
url: innerUrl
})
}, 1000); // 300
},
getStatistics() {
let paramsObj = {
repairType: this.repairTypeValueList[this.repairTypeIndex],
workType: this.workeTypeValueList[this.workTypeIndex],
ticketsStatus: this.ticketsStatusValueList[this.ticketsStatusIndex],
busiFrom: this.busiValueList[this.busiIndex],
...this.queryParams
}
request({
url: '/admin-api/repair/tickets/getStatistics',
params: paramsObj
}).then(res => {
this.statisticsInfo = res.data
})
},
/**
* 重置请求参数
*/
resetQueryParams() {
this.queryParams.pageNo = 1
this.queryParams.pageSize = 10
this.queryParams.search = null
this.queryParams.cusFrom = null
this.queryParams.ticketNo = null
this.repairTypeIndex = 0
this.workTypeIndex = 0
this.ticketsStatusIndex = 0
this.busiIndex = 0
this.payStatusIndex = 0
}
},
//
async onLoad(options) {
await this.setCurrentMonthRange()
await this.getDict()
await this.initDict("repair_type")
await this.initDict("repair_work_type")
if (options.selectType) {
this.ticketsStatusIndex = this.ticketsStatusValueList.indexOf(options.selectType);
}
if (options.repairType) {
this.repairTypeIndex = this.repairTypeValueList.indexOf(options.repairType);
}
if (options.payStatus) {
this.payStatusIndex = this.payStatusValueList.indexOf(options.payStatus);
}
this.initData()
this.queryBusiAndCus()
// #ifdef APP-PLUS
uni.showLoading({
title: "加载中..."
})
setTimeout(() => {
plus.screen.lockOrientation('default');
uni.hideLoading();
}, 1200)
// #endif
},
//
onUnload() {
// // 退
// #ifdef APP-PLUS
plus.screen.lockOrientation('portrait-primary'); //
plus.navigator.setFullscreen(false);
// #endif
},
//
onBackPress() {
console.log('执行跳转');
//
uni.redirectTo({
url: '/pages/white/white'
});
return true;
},
}
</script>
<style scoped>
.container {
width: 100%;
height: 100%;
background-color: white;
padding-left: env(safe-area-inset-left);
}
/* 顶部背景和标题 */
.header {
display: flex;
position: relative;
height: 100rpx;
justify-content: center;
align-items: center;
padding: 20rpx 40rpx;
padding-top: var(--status-bar-height); //
}
.bg-img {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
/* z-index: -1; */
}
.title {
text-align: center;
font-size: 36rpx;
font-weight: bold;
color: #333;
line-height: 100rpx;
z-index: 2;
}
/* 表格 */
.table {
margin: 20rpx;
border-radius: 12rpx;
overflow: hidden;
}
.table-row {
/* flex-direction: row;
display: flex; */
}
.table-header {
background-color: #f2f6ff;
font-weight: bold;
}
.table-cell {
flex: 1;
padding: 20rpx;
text-align: center;
font-size: 28rpx;
color: #555;
}
.row-bg {
background-color: #f9fbff;
}
.status {
color: #d4a017;
/* 金色 */
font-weight: 500;
}
/* 选项卡样式 */
.tabs {
display: flex;
margin-bottom: 30rpx;
padding-bottom: 20rpx;
}
.cont_time {
background: #F1F4F7;
border-radius: 36rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0px 30rpx;
width: 386rpx;
height: 56rpx;
background: #FFFFFF;
border-radius: 36rpx;
}
.cont_size {
font-weight: 400;
font-size: 24rpx;
color: #686C7A;
}
.tab {
margin-right: 40rpx;
font-size: 28rpx;
color: #666;
padding: 10rpx 0;
width: 56rpx;
height: 28rpx;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: 28rpx;
color: #707677;
line-height: 42rpx;
text-align: right;
font-style: normal;
}
.tab.active {
color: #007AFF;
font-weight: bold;
width: 56rpx;
height: 28rpx;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: bold;
font-size: 28rpx;
color: #292D2E;
line-height: 42rpx;
text-align: right;
font-style: normal;
margin-bottom: 20rpx;
}
.tap_img {
height: 3px;
text-align: center;
margin: 0 auto;
image {
width: 34rpx;
height: 12rpx;
}
}
.content_top {
width: 100%;
display: flex;
justify-content: space-between;
padding-left: 30rpx;
align-items: center;
margin-top: 30rpx;
}
.census {
display: flex;
justify-content: right;
font-weight: bold;
font-size: 30rpx;
/* margin: 1rem 1rem 1rem 0; */
}
.census>span {
padding-left: 3rem;
}
.credited {
color: green;
}
.notCredited {
color: gray;
}
.onlinePay {
color: blue;
}
.cashPay {
color: goldenrod;
}
.signedPay {
color: orange;
}
.content_right {
display: flex;
align-items: center;
}
.line {
width: 2rpx;
height: 60rpx;
background-color: #DDDDDD;
}
.tap_icon {
width: 40rpx;
height: 40rpx;
}
.search {
display: flex;
align-items: center;
}
.popup {
padding-top: var(--status-bar-height);
width: 700rpx;
height: 100vh;
display: flex;
flex-direction: column;
}
.popup-content {
flex: 1;
height: 100%;
overflow-y: auto;
padding: 20rpx;
box-sizing: border-box;
}
.border_ {
border: 1px solid #0357FF;
border-radius: 50rpx;
align-items: center;
}
.body-top-tab {
background: white;
display: flex;
font-size: 30rpx;
padding: 5rpx 0;
flex-direction: column;
}
.body-top-tab .line {
width: 2rpx;
height: 60rpx;
background-color: #DDDDDD;
}
.body-top-tab-item {
width: 100%;
padding: 20rpx 0;
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
}
.body-top-tab-item .label {
font-size: 28rpx;
color: #333;
}
.reset {
color: #165DFF;
padding: 20rpx;
display: flex;
align-items: center;
font-size: 28rpx;
}
/* 横屏适配 */
@media (orientation: landscape) {
.popup {
width: 60vw;
}
.popup-content {
max-height: 100vh;
}
}
</style>

View File

@ -0,0 +1,555 @@
<template>
<view class="detail-container">
<view class="top">
<view class="navbar">
<uni-icons type="left" color="#1f2d3d" size="22" @click="goBack" />
<text class="navbar-title">详情页</text>
</view>
<view class="header">
<!-- 选项卡 -->
<view class="tabs">
<view class="tab" :class="{'active':currentTab == index}" v-for="(item,index) in tapList"
:key="index" @click="switchTab(index)">
<view class="">{{item.label || "无"}}</view>
<view class="tap_img" style="display: block;" v-show="currentTab == index">
<image src="/static/images/icon_tap.png" mode="widthFix" class="tap_icon"></image>
</view>
</view>
</view>
<view class="">
<uni-datetime-picker v-model="queryParams.dateRange" type="daterange" @change="loadData()"
rangeSeparator="至" class="">
<view class="cont_time">
<view class="cont_size">{{queryParams.dateRange[0]}}</view>
<view class="bule_size"></view>
<view class="cont_size">{{queryParams.dateRange[1]}}</view>
<view class=""> <u-icon name="arrow-down-fill" color="#0357FF" size="12"></u-icon></view>
</view>
</uni-datetime-picker>
</view>
</view>
</view>
<view class="content">
<view class="section-list">
<view class="section-card" v-for="(sKey, idx) in statsData" :key="idx">
<view class="section-header" @click="goList(sKey.selectType)">
<view class="section-header-left">
<view class="section-tag">{{ sKey.name }}</view>
<view class="section-total">{{ sKey.total }}台次</view>
</view>
<view class="section-header-right">
<image src="../images/small.png" mode="widthFix" class="tap_icon" />
</view>
</view>
<view class="section-body">
<view class="kv" v-for="(item, i) in sKey.children" :key="i"
@click="goList(sKey.selectType,item.repairType)">
<view class="kv-number">{{ item.count }}</view>
<view class="kv-label">{{ dictData[item.repairType] }}</view>
</view>
</view>
</view>
</view>
<view class="finance" v-if="checkPermi(['repair:tick:profit'])">
<view class="fin-card receivable" @click.self="goListByPayStatus('receivable')">
<view class="fin-top">
<text class="fin-title">应收款</text>
<uni-icons :type="financeVisibility.receivable ? 'eye' : 'eye-slash'" color="#7aa6ff" size="20"
@click="toggleFinance('receivable')" />
</view>
<!-- 已收款金额 -->
<text
class="fin-amount">{{ financeVisibility.receivable ? formatCurrency(otherInfo.receivableAmount || 0) : '*****' }}</text>
<view class="fin-bottom">
<view class="fin-count">{{otherInfo.receivableCount}}台次</view>
<image src="../images/money_one.png" mode="widthFix" class="tap_icon"></image>
</view>
</view>
<view class="fin-card collected" @click.self="goListByPayStatus('receivedAmount')">
<view class="fin-top">
<text class="fin-title">已收款</text>
<uni-icons :type="financeVisibility.collected ? 'eye' : 'eye-slash'" color="#a896ff" size="20"
@click="toggleFinance('collected')" />
</view>
<text
class="fin-amount">{{ financeVisibility.collected ? formatCurrency(otherInfo.receivedAmount) : '*****' }}</text>
<view class="fin-bottom">
<view class="fin-count">{{otherInfo.receivedCount}}台次</view>
<image src="../images/money_two.png" mode="widthFix" class="tap_icon"></image>
</view>
</view>
<view class="fin-card pending" @click.self="goListByPayStatus('pendingAmount')">
<view class="fin-top">
<text class="fin-title">待收款</text>
<uni-icons :type="financeVisibility.pending ? 'eye' : 'eye-slash'" color="#ffcf7a" size="20"
@click="toggleFinance('pending')" />
</view>
<text
class="fin-amount">{{ financeVisibility.pending ? formatCurrency(otherInfo.pendingAmount) : '*****' }}</text>
<view class="fin-bottom">
<view class="fin-count">{{otherInfo.pendingCount}}台次</view>
<image src="../images/money_three.png" mode="widthFix" class="tap_icon"></image>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import request from '@/utils/request';
import {
formatCurrency,
getDictByCode,
getDateRange
} from '@/utils/utils';
import {
checkPermi,
checkRole
} from "@/utils/permission"; //
export default {
name: 'DetailPage',
data() {
return {
currentTab: 0,
queryParams: {
dateRange: [],
},
sectionOrder: ['orders', 'repairing', 'completed', 'settled', 'unsettled', 'delivered', 'inFactory'],
sectionTitle: {
orders: '订单(进厂)数',
repairing: '维修中',
completed: '已竣工',
settled: '竣工已结算',
unsettled: '竣工未结算',
delivered: '已交车',
inFactory: '在厂数'
},
tapList: [{
label: "本日",
value: "day",
},
{
label: "本月",
value: "month",
},
{
label: "全部",
value: "all",
},
],
statsData: {},
financeVisibility: {
receivable: false,
collected: false,
pending: false
},
dictData: undefined,
otherInfo: {}
};
},
async mounted() {
await this.setCurrentMonthRange()
this.getDict()
this.loadFinanceVisibility() //
this.loadData();
},
methods: {
checkPermi,
checkRole,
async switchTab(tab) {
if (this.currentTab !== tab) {
this.currentTab = tab;
await this.slectRange(tab)
}
},
slectRange(index) {
this.selected = index;
console.log(index, this.selected);
const {
value
} = this.tapList[index];
console.log(value);
this.queryParams.dateRange = getDateRange(value);
},
showDatePicker() {
console.log('show date picker');
},
//
loadFinanceVisibility() {
try {
const savedVisibility = uni.getStorageSync('financeVisibility');
if (savedVisibility) {
this.financeVisibility = savedVisibility;
}
} catch (e) {
console.error('Failed to load finance visibility from storage:', e);
}
},
// /
toggleFinance(key) {
this.financeVisibility[key] = !this.financeVisibility[key];
//
try {
uni.setStorageSync('financeVisibility', this.financeVisibility);
} catch (e) {
console.error('Failed to save finance visibility to storage:', e);
}
},
async getDict() {
const list = await getDictByCode('repair_type')
this.dictData = list?.reduce((map, item) => {
map[item.value] = item.label
return map
}, {}) ?? {} // list reduce undefined使
console.log('dict', this.dictData)
},
/**
* 列表
*/
goList(selectType, repairType) {
if (repairType) {
uni.navigateTo({
url: `/pages-business/statistics/statistics?selectType=${selectType}&repairType=${repairType}`
})
} else {
uni.navigateTo({
url: `/pages-business/statistics/statistics?selectType=${selectType}`
})
}
},
goListByPayStatus(payStatus) {
uni.navigateTo({
url: `/pages-business/statistics/statistics?payStatus=${payStatus}`
})
},
loadData() {
request({
url: '/admin-api/repair/tickets/getBossNumStatistics',
method: 'get',
params: this.queryParams
}).then(res => {
if (res.code === 200 && res.data) {
this.statsData = res.data.stats;
this.otherInfo = res.data
}
}).catch(error => {
console.error('Failed to load stats data:', error);
});
},
formatCurrency(amount) {
return formatCurrency(amount);
},
// toggleFinance(key) {
// this.financeVisibility[key] = !this.financeVisibility[key];
// },
goBack() {
uni.navigateBack();
},
setCurrentMonthRange() {
// 使 Date
const now = new Date();
// Date
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
this.queryParams.dateRange = [
this.formatDate(now), //
this.formatDate(now), //
];
},
formatDate(d) {
// d Date
if (!(d instanceof Date)) {
console.error("formatDate() 期望接收 Date 对象,但收到:", d);
d = new Date(d); // Date
if (isNaN(d.getTime())) {
//
d = new Date(); // 使
}
}
return `${d.getFullYear()}-${this.pad(d.getMonth() + 1)}-${this.pad(
d.getDate()
)}`;
},
// 1 01
pad(n) {
return n.toString().padStart(2, "0");
},
},
watch: {
currentTab() {
this.loadData();
}
}
};
</script>
<style scoped>
.detail-container {
/* background: #f2f6ff; */
min-height: 100vh;
padding-bottom: 24rpx;
height: 100%;
flex-direction: column;
overflow: hidden;
display: flex;
}
.navbar {
height: 88rpx;
display: flex;
align-items: center;
padding: 0 24rpx;
}
.top {
background-image: url('/static/images/table_header.png');
padding-top: var(--status-bar-height); //
}
.navbar-title {
flex: 1;
text-align: center;
color: #1f2d3d;
font-weight: 600;
font-size: 32rpx;
margin-right: 44rpx;
}
.header {
margin: 0 24rpx 24rpx;
/* background: linear-gradient(180deg, #e8f1ff 0%, #f5f8ff 100%); */
border-radius: 16rpx;
padding: 24rpx;
display: flex;
justify-content: space-between;
/* box-shadow: 0 8rpx 24rpx rgba(88, 131, 255, 0.12); */
}
.tabs {
display: flex;
gap: 24rpx;
margin-bottom: 20rpx;
}
.tab {
font-size: 28rpx;
color: #7f8fa4;
position: relative;
padding-bottom: 8rpx;
}
.tab.active {
color: #2a6cff;
font-weight: 600;
}
.tab.active:after {
/* content: '';
position: absolute;
left: 0;
bottom: 0;
width: 48rpx;
height: 6rpx;
background: #2a6cff;
border-radius: 6rpx; */
}
.date-range {
background: #ffffff;
border-radius: 12rpx;
padding: 18rpx 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
color: #5f6b7a;
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.04);
}
.date-text {
font-size: 26rpx;
}
.section-list {
padding: 0 24rpx;
display: flex;
flex-direction: column;
gap: 16rpx;
}
.section-card {
background: #ffffff;
border-radius: 16rpx;
box-shadow: 0 8rpx 28rpx rgba(31, 45, 61, 0.06);
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 8rpx;
background: linear-gradient(180deg, #EDF8FF 0%, #FBFEFF 100%);
}
.section-header-left {
display: flex;
}
.section-header-right {
image {
height: 30rpx;
width: 30rpx;
}
}
.section-tag {
background-image: url('../images/list_title.png');
color: #2a6cff;
font-size: 26rpx;
padding: 10rpx 18rpx;
font-weight: 600;
}
.section-total {
padding: 10rpx 18rpx;
font-size: 28rpx;
color: #144B7E;
font-weight: 500;
}
.section-body {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16rpx;
padding: 8rpx;
}
.kv {
border-radius: 12rpx;
padding: 18rpx 0;
text-align: center;
}
.kv-number {
color: #1f2d3d;
font-weight: 700;
font-size: 30rpx;
margin-bottom: 4rpx;
}
.kv-label {
color: #7f8fa4;
font-size: 22rpx;
}
.finance {
padding: 16rpx 24rpx 0;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16rpx;
}
.fin-card {
border-radius: 16rpx;
padding: 18rpx;
color: #1f2d3d;
display: flex;
flex-direction: column;
gap: 10rpx;
box-shadow: 0 10rpx 28rpx rgba(0, 0, 0, 0.06);
}
.fin-card.receivable {
background: linear-gradient(180deg, #eaf3ff, #f3f8ff);
}
.fin-card.collected {
background: linear-gradient(180deg, #f1ecff, #f7f4ff);
}
.fin-card.pending {
background: linear-gradient(180deg, #fff3df, #fff8ea);
}
.tap_img {
height: 3px;
text-align: center;
margin: 0 auto;
image {
width: 34rpx;
height: 12rpx;
}
}
.fin-top {
display: flex;
align-items: center;
justify-content: space-between;
}
.fin-title {
color: #7f8fa4;
font-size: 22rpx;
}
.fin-amount {
font-size: 32rpx;
font-weight: 700;
letter-spacing: 1rpx;
}
.fin-bottom {
display: flex;
align-items: center;
justify-content: space-between;
color: #7f8fa4;
font-size: 22rpx;
image {
height: 30rpx;
width: 30rpx;
}
}
.content {
overflow-y: scroll;
}
.cont_time {
background: #F1F4F7;
border-radius: 36rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0px 30rpx;
width: 386rpx;
height: 56rpx;
background: #FFFFFF;
border-radius: 36rpx;
}
.cont_size {
font-weight: 400;
font-size: 24rpx;
color: #686C7A;
}
.tap_icon {
width: 40rpx;
height: 40rpx;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

1589
pages-home/home/home1.vue Normal file

File diff suppressed because it is too large Load Diff

568
pages-home/home/index1.vue Normal file
View File

@ -0,0 +1,568 @@
<template>
<view class="content_">
<view class="topt_">
<view class="db_">
<view class="ds_">
<view class="user_img">
<image src="/static/icons/avatar.png" mode=""></image>
<view class="img_bo">蓝安汽修</view>
</view>
<view class="">
<view class="user_name">思利利</view>
<view class="user_gly">维修业务管理员</view>
</view>
</view>
<view class="top_time">
<view class="time_left">2015年08月12日</view>
<view class="time_right">星期二</view>
</view>
</view>
<view class="jg_">
<view class="jg_box">
<image src="/static/icons/order-icon3.png" mode=""></image>
<view class="">内部管理</view>
</view>
<view class="jg_box">
<image src="/static/icons/order-icon3.png" mode=""></image>
<view class="">业务管理</view>
</view>
<view class="jg_box">
<image src="/static/icons/order-icon3.png" mode=""></image>
<view class="">数据统计</view>
</view>
<view class="jg_box">
<image src="/static/icons/order-icon3.png" mode=""></image>
<view class="">消息通知</view>
</view>
</view>
</view>
<view class="bo_">
<view class="db_">
<view class="jg_left">
<view class="ds_">
<image src="/pages-home/static/waitingReceiveNum.png" mode=""></image>
<view class="">待审批工单</view>
</view>
<view class="">26</view>
</view>
<view class="jg_right">
<view class="ds_">
<image src="/pages-home/static/waitingReceiveNum.png" mode=""></image>
<view class="">待办工单</view>
</view>
<view class="">26</view>
</view>
</view>
<view class="db_">
<view class="three1">
<view class="">
<view class="t_size">进厂数</view>
<view class="t_num">123</view>
</view>
<image src="/pages-home/static/waitingReceiveNum.png" mode=""></image>
</view>
<view class="three2">
<view class="">
<view class="t_size">维修中</view>
<view class="t_num">123</view>
</view>
<image src="/pages-home/static/waitingReceiveNum.png" mode=""></image>
</view>
<view class="three3">
<view class="">
<view class="t_size">已竣工</view>
<view class="t_num">123</view>
</view>
<image src="/pages-home/static/waitingReceiveNum.png" mode=""></image>
</view>
</view>
<view class="db_c">
<view class="ds_">
<view class="gang_"></view>
<view class="title_">工单列表</view>
</view>
<view class="ds_h">查看全部</view>
</view>
<view class="c_box">
<view class="db_">
<view class="">
<view class="size1">川EPY528</view>
<view class="size2">东风</view>
</view>
<view class="">
<view class="size3">维修保养</view>
<view class="size4">进厂报修项目</view>
</view>
</view>
<view class="cont_title">
<view class="c_t_title">所有维修项目</view>
<view class="c_t_size">轿车二级维护发动机维修空调系统检修</view>
</view>
<view class="end_">
<view class="r_buttom">作废</view>
<view class="l_buttom">展开</view>
<view class="l_buttom">查看详情</view>
</view>
</view>
</view>
</view>
</template>
<script>
import {getToken,getUserInfo} from "@/utils/auth";
import request from '@/utils/request';
let innerAudioContext ='';
const keepAlivePlugin = uni.requireNativePlugin('Ba-KeepAlive')
export default {
data() {
return {
//
ifPlay:false,
keepLive: {
channelId: 'Ba-KeepAlive',
channelName: "Ba-KeepAlive",
title: "汽修小助手正在运行",
content: "汽修小助手正在运行",
dataResult: "",
type: undefined
}
}
},
onLoad() {
// #ifdef APP
//
this.register()
// #endif
if (getToken()) {
this.getNoReadNum()
//
//
let userInfo = getUserInfo()
if (userInfo.roleCodes.includes('repair_warehouse')) {
//
uni.navigateTo({
url: '/pages-warehouse/home/home'
})
} else {
uni.navigateTo({
url: '/pages-home/home/home'
})
}
} else {
uni.navigateTo({
url: '/pages/login/login'
})
}
},
onShow(){
if (getToken()) {
//
let userInfo = getUserInfo()
if (userInfo.roleCodes.includes('repair_warehouse')) {
//
uni.navigateTo({
url: '/pages-warehouse/home/home'
})
} else {
uni.navigateTo({
url: '/pages-home/home/home'
})
}
} else {
uni.navigateTo({
url: '/pages/login/login'
})
}
},
methods: {
register() { //
console.log(keepAlivePlugin, 'keepAlive');
keepAlivePlugin.register({
channelId: this.keepLive.channelId,
channelName: this.keepLive.channelName,
title: this.keepLive.title,
content: this.keepLive.content,
},
(res) => {
console.log('保活注册', res);
});
},
/**
* 获取未读消息数量
* @returns {Promise<void>}
*/
async getNoReadNum(){
await request({
url: "/admin-api/system/notify-message/get-unread-count?systemCode=weixiu",
method: "GET"
}).then((res) => {
if(res.code==200 && res.data>0){
this.dianyidain()
}
})
},
dianyidain() {
if(!this.ifPlay){
console.log('执行了dianyidain');
if(innerAudioContext!=""){
try {
console.log('调用前先销毁');
innerAudioContext.stop();
innerAudioContext.destroy();
innerAudioContext="";
}catch (e){
console.log('销毁出错');
}
}
innerAudioContext = uni.createInnerAudioContext();
this.ifPlay=true
// #ifdef APP-PLUS
innerAudioContext.src = '../../static/msgV.mp3';
// #endif
// #ifndef APP-PLUS
innerAudioContext.src = 'https://www.nuoyunr.com/lananRsc/rescue/msgV.mp3';
// #endif
//
const playCount = 4;
let currentCount = 0;
//
innerAudioContext.play();
//
uni.vibrateLong({
success: function () {
console.log('success');
}
});
innerAudioContext.onError((err) => {
console.error('播放错误', err);
this.ifPlay=false
innerAudioContext.stop();
innerAudioContext.destroy(); //
});
//
innerAudioContext.onEnded(() => {
//
currentCount++;
//
if (currentCount < playCount) {
//
innerAudioContext.play();
//
uni.vibrateLong({
success: function () {
console.log('success');
}
});
} else {
//
console.log('播放完成');
this.ifPlay=false
//
innerAudioContext.stop();
innerAudioContext.destroy();
}
});
}else{
console.log('正在播放音频,拒绝播放请求');
}
},
}
}
</script>
<style lang="scss" scoped>
.content_{
}
.topt_{
width: 100%;
height: 408rpx;
background: linear-gradient( 180deg, #2D3BFF 0%, #1463FF 100%);
border-radius: 0rpx 0rpx 12rpx 12rpx;
box-sizing: border-box;
padding: 30rpx;
padding-top: 96px;
}
.db_{
display: flex;
align-items: center;
justify-content: space-between;
}
.db_c{
display: flex;
align-items: center;
justify-content: space-between;
margin: 30rpx auto;
}
.ds_{
display: flex;
align-items: center;
}
.top_time{
display: flex;
align-items: center;
justify-content: space-between;
}
.time_left{
width: 210rpx;
height: 52rpx;
background: rgba(255,255,255,0.4);
border-radius: 36rpx 0rpx 0rpx 36rpx;
background: #7e90fe;
color: #fff;
}
.time_right{
background: #fff;
color: #214FFF;
border-radius: 36rpx 0rpx 0rpx 36rpx;
width: 118rpx;
height: 52rpx;
}
.user_gly{
font-weight: 400;
color: rgba(255,255,255,0.83);
font-size: 24rpx;
}
.user_name{
font-weight: bold;
font-size: 28rpx;
color: #FFFFFF;
margin-bottom: 5rpx;
}
.user_img{
position: relative;
text-align: center;
image{
width: 96rpx;
height: 96rpx;
}
}
.img_bo{
width: 116rpx;
height: 32rpx;
background: linear-gradient( 180deg, #8DB4FF 0%, #3069FF 100%);
border-radius: 20rpx;
border: 1rpx solid rgba(255,255,255,0.46);
font-weight: 500;
font-size: 24rpx;
color: #FFFFFF;
}
.jg_{
display: flex;
align-items: center;
justify-content: space-between;
}
.jg_box{
text-align: center;
font-weight: 400;
font-size: 28rpx;
color: #FFFFFF;
width: 25%;
image{
width: 56rpx;
height: 56rpx;
margin-bottom: 10rpx;
}
}
.bo_{
width: 100%;
box-sizing: border-box;
padding: 30rpx;
}
.jg_left{
width: 356rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding: 30rpx;
color: #fff;
font-weight: bold;
font-size: 28rpx;
color: #FFFFFF;
background: linear-gradient( 68deg, #6CAFFF 0%, #0059FF 100%);
image{
width: 40rpx;
height: 40rpx;
margin-right: 15rpx;
}
}
.jg_right{
width: 356rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding: 30rpx;
color: #fff;
font-weight: bold;
font-size: 28rpx;
color: #FFFFFF;
background: linear-gradient( 68deg, #87E2FF 0%, #008BFF 100%);
image{
width: 40rpx;
height: 40rpx;
margin-right: 15rpx;
}
}
.three1{
width: 234rpx;
height: 120rpx;
background: linear-gradient( 68deg, #EEF5FF 0%, #D7E0FC 100%);
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding: 10rpx;
padding-left: 30rpx;
image{
width: 96rpx;
height: 92rpx;
}
}
.three2{
width: 234rpx;
height: 120rpx;
background: linear-gradient( 84deg, #E3F2F5 0%, #AFEEF8 100%);
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding: 10rpx;
padding-left: 30rpx;
image{
width: 96rpx;
height: 92rpx;
}
}
.three3{
width: 234rpx;
height: 120rpx;
background: linear-gradient( 84deg, #E8F2E1 0%, #CCEFDD 100%);
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding: 10rpx;
padding-left: 30rpx;
image{
width: 96rpx;
height: 92rpx;
}
}
.t_size{
font-weight: 400;
font-size: 28rpx;
color: #284F89;
margin-bottom: 10rpx;
}
.t_num{
font-weight: bold;
font-size: 28rpx;
color: #3E4045;
}
.gang_{
width: 8rpx;
height: 40rpx;
background: #195AFF;
margin-right: 8rpx;
border-radius: 8px;
}
.title_{
font-weight: 800;
font-size: 28rpx;
color: #23252B;
}
.ds_h{
display: flex;
align-items: center;
font-weight: 400;
font-size: 28rpx;
color: #9799A1;
}
.c_box{
width: 100%;
height: 300rpx;
background: #FFFFFF;
border-radius: 8rpx;
box-sizing: border-box;
padding: 30rpx;
margin: 10px auto;
}
.size1{
font-weight: 600;
font-size: 28rpx;
color: #3B3D41;
}
.size2{
font-weight: 400;
font-size: 24rpx;
color: #686B71;
}
.size3{
font-weight: 600;
font-size: 28rpx;
color: #3B3D41;
}
.size4{
font-weight: 400;
font-size: 24rpx;
color: #176C97;
}
.cont_title{
height: 98rpx;
background: #F7F8F9;
border-radius: 8rpx;
width: 100%;
box-sizing: border-box;
padding: 10rpx;
margin: 10rpx auto;
}
.c_t_title{
font-weight: 400;
font-size: 24rpx;
color: #176C97;
}
.c_t_size{
font-weight: 600;
font-size: 24rpx;
color: #3B3D41;
}
.end_{
display: flex;align-items: center;justify-content: end;
}
.l_buttom{
width: 168rpx;
height: 48rpx;
background: #FFFFFF;
border-radius: 30rpx;
border: 2rpx solid #2E90F2;
color: #2E90F2;
display: flex;
align-items: center;
justify-content: center;
font-weight: 400;
font-size: 28rpx;
color: #2E90F2;
margin-left: 10rpx;
}
.r_buttom{
width: 168rpx;
height: 48rpx;
background: #FFFFFF;
border-radius: 30rpx;
border: 2rpx solid #F2612E;
color: #F2612E;
display: flex;
align-items: center;
justify-content: center;
font-weight: 400;
font-size: 28rpx;
margin-left: 10rpx;
}
</style>

View File

@ -0,0 +1,158 @@
<template>
<view class="">
<!-- <view class="top-rail"></view>
<view class="page-top" :style='{ justifyContent: "center" }'>
<view class='go-back-page' @click='pageBack'>返回</view>
<view class="other-title-class">
设备管理
</view>
</view> -->
<VNavigationBar style="position: relative;z-index: 99;" homeHeaderPaddingTop="0" backgroundColor="#fff"
title-color="#000" title="设备管理"></VNavigationBar>
<view class="content">
<view class="box_">
<view class="box_list" v-for="(item, index) in tabList">
<view class="" @click="gettap(item)">
<image :src=defaultIcon mode=""></image>
<!-- <image :src=iconArr[index] mode=""></image> -->
<view class="name_">{{ item.categoryName }}</view>
<view class="">{{ item.count }}</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import {
getDictDataByType
} from "@/utils/utils";
import request from "@/utils/request";
import VNavigationBar from "@/components/VNavigationBar.vue";
export default {
data() {
return {
titles: "设备管理",
List: [],
show: false,
status: 'loading',
tabList: [],
/* iconArr: ["../../static/internal/s1.png", "../../static/internal/s2.png", "../../static/internal/s3.png",
"../../static/internal/s4.png"
] */
defaultIcon: "../../static/icons/internal/s1.png"
}
},
components: {
VNavigationBar
},
onLoad() {
// this.getTab()
this.getCount()
},
methods: {
gettap(value) {
uni.navigateTo({
url: `/pages-internal/deviceManagement/informationManage?value=${value.categoryId}`
})
},
async getTab() {
const data = await getDictDataByType("repair_equ_type");
this.tabList = [...this.tabList, ...data];
},
pageBack() {
uni.navigateBack({
delta: 1 // delta1
});
},
getCount() {
const data = request({
url: '/admin-api/system/equInfo/queryEquipmentCountByPackageId',
method: 'get',
params: {
servicePackageId: 'weixiu',
dictType: 'repair_equ_type'
}
});
this.tabList = data.then(res => {
this.tabList = res.data
})
},
},
}
</script>
<style scoped lang="scss">
.content {
/* background: #F7F8FC;
width: 100%;
height: calc(100vh - 220rpx);
box-sizing: border-box;
padding: 30rpx; */
// padding-top: 200rpx;
background: #F7F8FC;
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 30rpx;
}
.box_ {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-top: 40rpx;
}
.box_list {
width: 328rpx;
height: 396rpx;
background: #fff;
border-radius: 18px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
margin-bottom: 30rpx;
image {
width: 130rpx;
height: 130rpx;
}
}
.name_ {
font-size: 28rpx;
color: #000000;
}
.num_ {
font-size: 36rpx;
color: #000000;
}
/* .content {
width: 100%;
box-sizing: border-box;
background: #f4f5f6;
// background: #55aaff;
// height: 100vh;
} */
.top-rail {
height: 60rpx;
width: 100%;
background-color: #054DF3;
}
</style>

View File

@ -0,0 +1,580 @@
<!-- 发起订单 -->
<template>
<view class="content">
<!-- <view class="top-rail"></view>
<view class="c-top" :style='{ justifyContent: "center" }'>
<view class="go-back-page" @click="getback()">返回</view>
<view class="c-title" v-if="type == 'add'">新增设备</view>
<view class="c-title" v-if="type == 'edit'">编辑设备</view>
<view class=""></view>
</view> -->
<!-- <headersVue :titles="pageName" style="position: static !important;">
<u-icon name="arrow-left" color="#fff" size="18"></u-icon>
</headersVue> -->
<VNavigationBar style="position: relative;z-index: 99;" homeHeaderPaddingTop="0" backgroundColor="#fff"
title-color="#000" :title="pageName"></VNavigationBar>
<view class="dil">
<view class="tinput">
<view class="text1">
<text class="hong1">*</text>
设备名称
</view>
<view class="you">
<input type="text" placeholder="请输入设备名称" v-model="box.equName">
</view>
</view>
<view class="tinput">
<view class="text1">
<text class="hong1">*</text>
设备型号
</view>
<view class="you">
<input type="text" placeholder="请输入设备型号" v-model="box.equModel">
</view>
</view>
<view class="tinput">
<view class="text1">
<text class="hong1">*</text>
设备编号
</view>
<view class="you">
<input type="text" placeholder="请输入设备编号" v-model="box.equNumber">
</view>
</view>
<view class="tinput" @click="typeShow = true">
<view class="text1">
设备类别
</view>
<view class="you">
<input type="text" disabled placeholder="请选择设备类别" v-model="typeName">
</view>
</view>
<view class="tinput">
<view class="text1">
<text class="hong1">*</text>
设备检定周期
</view>
<view class="you">
<input type="text" placeholder="请输入设备检定周期" v-model="box.equZq">
</view>
</view>
<view class="tinput">
<view class="text1">
<text class="hong1">*</text>
上次检定单位
</view>
<view class="you">
<input type="text" placeholder="请输入上次检定单位" v-model="box.lastUnit">
</view>
</view>
<view class="tinput">
<view class="text1">
<text class="hong1">*</text>
有效期
</view>
<!-- validTime -->
<view class="you" @click="show = true ">
<text>{{ time || '--' }}</text>
</view>
</view>
<view class="tinput">
<view class="text1">
<text class="hong1">*</text>
下次检定时间
</view>
<!-- nextCheckTime -->
<view class="you" @click="show1 = true ">
<text>{{ time1 || '--' }}</text>
</view>
</view>
<view class="tinput" v-if="type == 'edit'">
<view class="text1">
<text class="hong1">*</text>
设备附件
</view>
<view class="lan_" @click="goFile">查看附件
<image src="../../static/icons/internal/add.png" mode=""></image>
</view>
</view>
<u-datetime-picker :show="show" v-model="box.validTime" @cancel="cancels" @confirm="confirms" mode="date"
return-type='string'></u-datetime-picker>
<u-datetime-picker :show="show1" v-model="box.nextCheckTime" @cancel="cancels1" @confirm="confirms1"
mode="date" return-type='string'></u-datetime-picker>
<u-picker :show="typeShow" :columns="columns" @confirm="confirmsType" @cancel="typeShow = false"
keyName="label"></u-picker>
<view class="anniu" @click="getnewsadd()">
<text>保存</text>
</view>
<view style="width: 100%; height: 60px;"></view>
</view>
</view>
</template>
<script>
import request from '../../utils/request'
import config from '@/config'
import {
getToken
} from '@/utils/auth'
import upload from '@/utils/upload.js'
import {
getDictByCode,
getDictDataByType
} from "../../utils/utils";
import VNavigationBar from "@/components/VNavigationBar.vue";
var wvCurrent;
export default {
components: {
VNavigationBar
},
data() {
return {
time: '请选择有效期',
time1: '请选择下次检定时间',
value1: '2023-10-10',
baseUrl: this.$baseUrl,
show: false,
show1: false,
fileId: null,
id: '',
type: 'add',
box: {},
typeShow: false,
columns: [],
typeName: null,
//
fileList3: [],
pageName: '新增设备',
baseImageUrl: config.baseImageUrl,
}
},
onLoad(option) {
this.type = option.type
if (option.type == 'edit') {
this.box.id = option.id
this.pageName = '编辑设备'
}
this.getInsType()
},
onShow() {
if (this.type == 'edit') this.getDetail();
},
methods: {
confirmsType(e) {
console.log(e)
this.box.type = e.value[0].value
this.typeName = e.value[0].label
this.typeShow = false
},
async getInsType() {
if (!this.columns || this.columns.length === 0) {
this.columns = [await getDictByCode("repair_equ_type")]
}
console.log(this.columns)
},
async getDetail() {
let res = await request({
url: '/admin-api/system/equInfo/' + this.box.id,
method: 'get'
})
if (res.code == 200) {
this.box = res.data
if (this.box.fileList) {
this.fileList3 = this.box.fileList
// URL
this.fileList3 = this.fileList3.map(file => {
//
file.url = this.baseImageUrl + '/' + file.url;
return file;
});
}
// this.maneizhi = this.box.type
this.time = this.box.validTime
this.time1 = this.box.nextCheckTime
}
if (this.box.type) {
const data = this.columns[0]
const index = data.findIndex(item => item.value === this.box.type)
if (index !== -1) {
this.typeName = data[index].label
}
}
},
cancels() {
this.show = false
},
cancels1() {
this.show1 = false
},
confirms(e) {
var timestamp = e.value;
var date = new Date(timestamp);
var year = date.getFullYear();
var month = ("0" + (date.getMonth() + 1)).slice(-2);
var day = ("0" + date.getDate()).slice(-2);
var formattedDate = year + "-" + month + "-" + day;
this.box.validTime = formattedDate
this.time = formattedDate
this.show = false
},
confirms1(e) {
var timestamp = e.value;
var date = new Date(timestamp);
var year = date.getFullYear();
var month = ("0" + (date.getMonth() + 1)).slice(-2);
var day = ("0" + date.getDate()).slice(-2);
var formattedDate = year + "-" + month + "-" + day;
this.box.nextCheckTime = formattedDate
this.time1 = formattedDate
this.show1 = false
},
//
async getnewsadd() {
this.box.fileList = this.fileList3.map(file => {
//
if (file.url && file.url.startsWith(this.baseImageUrl)) {
file.url = file.url.replace(this.baseImageUrl, '');
}
return file;
});
if (this.type == 'add') {
this.box.servicePackageId = 'wexiu'
let res = await request({
url: '/admin-api/system/equInfo/add',
method: 'post',
data: this.box
})
if (res.code == 200) {
uni.showToast({
title: '发布成功'
})
setTimeout(() => {
uni.navigateBack({
delta: 1
});
}, 1000);
}
}
if (this.type == 'edit') {
let res = await request({
url: '/admin-api/system/equInfo/edit',
method: 'post',
data: this.box
})
if (res.code == 200) {
uni.showToast({
title: '发布成功'
})
setTimeout(() => {
uni.navigateBack({
delta: 1
});
}, 1000);
}
}
},
getback() {
uni.navigateBack({
delta: 1,
})
},
//
deletePic(event) {
this[`fileList${event.name}`].splice(event.index, 1);
},
//
async afterRead(event) {
// multiple true , file
let lists = [].concat(event.file);
let fileListLen = this[`fileList${event.name}`].length;
lists.map((item) => {
this[`fileList${event.name}`].push({
...item,
status: "uploading",
message: "上传中",
});
});
for (let i = 0; i < lists.length; i++) {
const result = await this.uploadFilePromise(lists[i].url);
let item = this[`fileList${event.name}`][fileListLen];
this[`fileList${event.name}`].splice(
fileListLen,
1,
Object.assign(item, {
status: "success",
message: "",
url: result,
})
);
fileListLen++;
}
console.log('现在文件集合', this[`fileList${event.name}`])
},
uploadFilePromise(url) {
return new Promise((resolve, reject) => {
upload({
url: '/common/upload',
filePath: url,
}).then((res) => {
resolve(res.data.url);
})
});
},
goFile() {
if (!this.box.id) {
uni.showToast({
title: '请先新建设备',
icon: 'none'
})
return
}
if (!this.box.folderId) {
console.log('当前员工没有文件加')
//
request({
url: '/admin-api/system/equInfo/addDeviceFolder',
method: 'post',
params: {
id: this.box.id,
servicePackageId: 'jiuyuan',
defaultKey: 'jy_equipment'
},
}).then(res => {
this.box.folderId = res.data
uni.navigateTo({
url: '/pages-internal/dataManagement/deviceManage?type=staff&folderId=' + this
.box
.folderId
})
})
} else {
uni.navigateTo({
url: '/pages-internal/dataManagement/deviceManage?type=staff&folderId=' + this.box.folderId
})
}
},
}
}
</script>
<style scoped lang="scss">
.content {
/* width: 100%;
height: calc(100vh);
background-color: #F6F6F6;
box-sizing: border-box; */
// padding-top: 45px;
width: 100%;
height: 100vh;
background-color: #F6F6F6;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.dil {
/* box-sizing: border-box;
background-color: #F6F6F6;
padding: 0px 12px; */
box-sizing: border-box;
background-color: #F6F6F6;
padding: 0px 12px;
flex: 1;
overflow-y: auto;
}
.lan_ {
font-size: 28rpx;
color: #327DFB;
display: flex;
align-items: center;
image {
width: 36rpx;
height: 36rpx;
margin-left: 15rpx;
}
}
.top-icon {
margin-bottom: 45px;
}
.c-top {
width: 100%;
box-sizing: border-box;
padding: 15px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: white;
padding-top: 40px;
}
.c-title {
font-size: 18px;
font-weight: bold
}
.top {
box-sizing: border-box;
padding: 0px 15px;
width: 100%;
background-color: white;
}
.top-box {
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
padding: 20px 0px;
}
.tb-left {
height: 100%;
width: 80%;
display: flex;
align-items: center;
}
.uicon {
width: 18px;
height: 18px;
border-radius: 4px;
color: white;
background: orangered;
display: flex;
justify-content: center;
align-items: center;
font-size: 12px;
font-weight: bold;
margin-right: 6px;
}
.tb-right {
width: 20px;
height: 26px;
image {
width: 100%;
height: 100%;
}
}
.text1 {
font-size: 16px;
font-weight: bold;
color: #363636;
}
.hong1 {
margin-top: 5px;
font-size: 12px;
font-weight: 400;
color: #FF5453;
}
.hong2 {
margin-top: 5px;
font-size: 12px;
font-weight: 400;
}
.tinput {
width: 100%;
display: flex;
background: white;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding: 16px;
margin-top: 14px;
margin-bottom: 14px;
}
.xinput {
width: 100%;
background: white;
box-sizing: border-box;
padding: 16px;
margin-top: 14px;
}
.you {
text-align: right;
}
.xz {
margin-top: 10px;
display: flex;
align-items: center;
}
.kuang {
width: 80px;
height: 23px;
background: #ECECEC;
border-radius: 5px;
display: flex;
justify-content: center;
align-items: center;
color: #666666;
font-size: 14px;
margin-right: 15px;
}
.xlan {
background: #CDE7FF !important;
color: #1D62FF !important;
border: 1px solid #2A96FE;
}
.anniu {
width: 100%;
//background: linear-gradient(180deg, #3F61C0 0%, #0D2E8D 100%);
color: #0174F6;
border-radius: 50px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
//color: #542F0E;
border: 1px solid #0174F6;
margin-top: 20px;
}
button::after {
border: none;
}
</style>

View File

@ -0,0 +1,310 @@
<template>
<view class="content">
<VNavigationBar style="position: relative;z-index: 99;" homeHeaderPaddingTop="0" backgroundColor="#fff"
title-color="#000" title="设备管理"></VNavigationBar>
<!-- 固定部分搜索框 -->
<view class="search_box">
<u-search placeholder="请输入设备名称..." @clear="getlistindex()" @search="getlistindex()" :showAction="false"
v-model="equName" searchIconColor="#427FFE"></u-search>
</view>
<!-- 固定部分新增按钮 -->
<view class="add-btn" @click="goadd('add')">
<text>+ 新增设备</text>
</view>
<!-- 固定部分Tab切换横向滚动 -->
<view class="tab-scroll-container">
<scroll-view class="tab-scroll-view" scroll-x="true" shows-horizontal-scroll-indicator="false">
<view class="tap-box" v-for="(item,index) in tabList" :key="index" @click="gettap(item.value)">
<view :class="{'lan' : tapindex == item.value}">{{ item.label }}</view>
<view class="gang" v-if="tapindex == item.value"></view>
</view>
</scroll-view>
</view>
<!-- 滚动部分设备列表 -->
<view class="list-container">
<!-- 有数据时显示列表 -->
<view class="c-box" v-for="(item,index) in arrlist" :key="index" v-if="arrlist.length > 0">
<view class="box-top">
<view>{{ item.equName || '设备名称' }}</view>
<view style="display: flex;align-items: center; justify-content: space-between; width: 25%;">
<view style="color: #43A045;" @click="goadd('edit',item.id)">编辑</view>
<view style="color: #FF7272;" @click="dialogToggle(item.id)">删除</view>
</view>
</view>
<view class="box-hui">
<view class="box-left">设备型号:</view>
<view>{{ item.equModel || '' }}</view>
</view>
<view class="box-hui">
<view class="box-left">设备编号:</view>
<view>{{ item.equNumber || '' }}</view>
</view>
<view class="box-hui">
<view class="box-left">检定/校准周期:</view>
<view>{{ item.equZq || '' }}</view>
</view>
<view class="box-hui">
<view class="box-left">有效期:</view>
<view>{{ item.validTime || '' }}</view>
</view>
<view class="box-hui">
<view class="box-left">检定单位:</view>
<view>{{ item.lastUnit || '' }}</view>
</view>
<view class="box-hui">
<view class="box-left">计划检定时间:</view>
<view>{{ item.nextCheckTime || '' }}</view>
</view>
</view>
<!-- 无数据时显示提示 -->
<view class="empty-tips" v-if="arrlist.length === 0">
<image src="/static/internal/empty.png" class="empty-img"></image>
<!-- <text>暂无设备</text> -->
</view>
</view>
<uni-popup ref="alertDialog" type="dialog">
<uni-popup-dialog cancelText="关闭" confirmText="同意" title="通知" content="您确认要删除吗" @confirm="dialogConfirm"
@close="dialogClose"></uni-popup-dialog>
</uni-popup>
</view>
</template>
<script>
import config from '@/config'
import request from '../../utils/request';
import VNavigationBar from "@/components/VNavigationBar.vue";
import {
getDictByCode
} from "../../utils/utils";
export default {
components: {
VNavigationBar
},
data() {
return {
partnerId: '',
arrlist: [],
pageNum: 1,
pageSize: 20,
totalPages: 0,
deleteid: '',
equName: null,
tabList: [{
label: "全部",
value: "0"
}],
tapindex: 0,
type: null
}
},
onLoad(data) {
this.partnerId = uni.getStorageSync('partnerId')
if (data.value) {
this.tapindex = parseInt(data.value)
this.type = data.value === '0' ? null : data.value
}
this.getlistindex()
this.getTab()
},
onShow() {
// this.getlistindex()
},
onReachBottom() {
if (this.pageNum >= this.totalPages) {
uni.showToast({
title: '没有下一页数据',
icon: 'none'
})
} else {
this.pageNum++
this.getlistindex()
}
},
methods: {
gettap(value) {
this.tapindex = value
this.type = value === '0' ? null : value
this.getlistindex()
},
async getTab() {
const data = await getDictByCode("repair_equ_type");
console.log('data123', data)
this.tabList = [...this.tabList, ...data];
console.log('this.tabList', this.tabList)
},
dialogToggle(id) {
this.deleteid = id
this.$refs.alertDialog.open()
},
async dialogConfirm() {
let res = await request({
url: '/admin-api/system/equInfo/' + this.deleteid,
method: 'delete',
})
if (res.code == 200) {
this.$refs.alertDialog.close()
uni.showToast({
icon: 'none',
title: '删除成功'
})
this.getlistindex()
}
},
dialogClose() {
this.$refs.alertDialog.close()
},
goadd(type, id) {
uni.navigateTo({
url: '/pages-internal/deviceManagement/informationAdd?type=' + type + '&id=' + id
})
},
async getlistindex() {
let res = await request({
url: '/admin-api/system/equInfo/list',
method: 'get',
data: {
pageSize: this.pageSize,
pageNum: this.pageNum,
equName: this.equName,
type: this.type,
servicePackageId: 'weixiu'
}
})
if (this.pageNum != 1) {
this.arrlist = this.arrlist.concat(res.rows)
} else {
this.arrlist = res.rows
}
let total = res.total
this.totalPages = Math.ceil(total / this.pageSize);
},
}
}
</script>
<style scoped lang="scss">
.content {
width: 100%;
height: 100vh;
background-color: white;
display: flex;
flex-direction: column;
}
/* 固定搜索框 */
.search_box {
padding: 10px 15px;
background: white;
border-bottom: 1px solid #f5f5f5;
}
/* 固定新增按钮 */
.add-btn {
width: 100%;
color: #0174F6;
text-align: center;
padding: 10px 0;
font-weight: bold;
border-radius: 8px;
margin: 10px 15px;
width: calc(100% - 30px);
border: 1px solid #0174F6;
position: sticky;
top: 50px;
z-index: 9;
}
/* Tab横向滚动容器 */
.tab-scroll-container {
background: white;
position: sticky;
top: 100px;
z-index: 8;
border-bottom: 1px solid #f5f5f5;
padding: 10px 0;
}
.tab-scroll-view {
white-space: nowrap;
width: 100%;
height: 40px;
}
.tap-box {
display: inline-block;
min-width: 80px;
text-align: center;
padding: 0 10px;
}
.gang {
height: 4px;
background: #327DFB;
width: 80%;
margin: 0 auto;
}
.lan {
color: #0D2E8D;
}
/* 滚动列表容器 */
.list-container {
flex: 1;
overflow-y: auto;
padding: 0 15px;
}
/* 列表项样式 */
.c-box {
background: #F7F8FC;
border-radius: 10px;
padding: 10px;
margin-bottom: 10px;
}
.box-top {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
margin-bottom: 10px;
font-weight: bold;
}
.box-hui {
display: flex;
margin-bottom: 5px;
color: #999;
font-size: 14px;
}
.box-left {
margin-right: 10px;
}
/* 空列表提示 */
.empty-tips {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 0;
color: #999;
font-size: 14px;
}
.empty-img {
width: 240px;
height: 200px;
margin-bottom: 10px;
}
</style>

26
pages/white/white.vue Normal file
View File

@ -0,0 +1,26 @@
<template>
<view>
</view>
</template>
<script>
export default {
name: 'White',
data() {
return {
}
},
onLoad() {
uni.navigateBack()
},
methods: {
}
}
</script>
<style>
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

BIN
static/images/icon_tap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

BIN
static/images/j1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
static/images/j2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
static/images/j3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

BIN
static/images/j4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
static/images/r1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 B

BIN
static/images/r2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
static/images/t1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/t2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/t3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
static/images/ttel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 B

BIN
static/images/yh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

26
utils/fixScreen.js Normal file
View File

@ -0,0 +1,26 @@
// common/fixScreen.js
/**
* 修复 APP 端横竖屏切换后页面元素放大问题
* 使用方式在页面 onShow 中调用 fixScreen()
*/
export function fixScreen() {
// #ifdef APP-PLUS
try {
console.log('执行缩放比');
const pages = getCurrentPages();
const page = pages[pages.length - 1];
const currentWebview = page.$getAppWebview();
if (currentWebview) {
// 强制重置缩放比例,避免放大
currentWebview.setStyle({
scalable: false, // 禁止缩放
viewport: "device-width" // 保持设备宽度
});
}
} catch (e) {
console.error("fixScreen 执行失败:", e);
}
// #endif
}