Jelajahi Sumber

服务查询列表

xiaohaizhao 8 bulan lalu
induk
melakukan
dd6960cf57

+ 12 - 0
pages.json

@@ -15,6 +15,18 @@
 		{
 			"path": "pages/index/index"
 		},
+		{
+			"path": "pages/serviceSeeking/index",
+			"style": {
+				"navigationBarTitleText": "服务查询"
+			}
+		},
+		{
+			"path": "pages/serviceSeeking/detail",
+			"style": {
+				"navigationBarTitleText": "服务详情"
+			}
+		},
 		{
 			"path": "pages/bookingService/index",
 			"style": {

+ 7 - 10
pages/bookingService/index.vue

@@ -139,14 +139,12 @@ import { ref, reactive, getCurrentInstance } from 'vue';
 const { $Http } = getCurrentInstance().proxy;
 import { onShow } from '@dcloudio/uni-app';
 
-const userMsg = uni.getStorageSync('userMsg'),
-    userRecord = uni.getStorageSync('userRecord');
+const userMsg = uni.getStorageSync('userMsg');
 const uFormRef = ref(null);
 const form = reactive({
     sa_serviceorderid: 0,
     sa_orderid: 0,
     sys_enterpriseid: 0,
-    sa_customersid: userRecord.sa_customersid || 0,
     servicetype: '', // 服务类型
     class1: '', // 产品品类
     class2: '', // 故障类型
@@ -224,13 +222,14 @@ function save() {
             content: '请确认预约信息正确以便后续服务,是否确认提交?',
             title: '提示',
             success: ({ confirm }) => {
-
+                let userRecord = uni.getStorageSync('userRecord') || { sa_customersid: "" };
                 form.customername = userRecord.name || form.scenecontact;
                 form.customerphonenumber = userRecord.phonenumber || form.scenecontactphonenumber;
                 form.name = form.customername;
                 form.phonenumber = userRecord.phonenumber || form.scenecontactphonenumber;
+                form.sa_customersid = userRecord.sa_customersid
                 let content = {
-                    ...form
+                    ...form,
                 };
                 loading.value = true;
                 $Http.basic({
@@ -271,7 +270,7 @@ let querySku = ref(true); // SKU是否正确
 
 function skuConfirm() {
     if (form.sku) {
-        ['contact', 'serviceenterprisename', 'cardno', 'itemid', 'itemname', 'itemno', 'model', 'phonenumber', 'unitname', 'spec'].forEach(key => {
+        ['contact', 'serviceenterprisename', 'cardno', 'itemid', 'itemname', 'itemno', 'model', 'phonenumber', 'unitname', 'spec', 'sys_enterpriseid'].forEach(key => {
             form[key] = '';
         });
         if (form.sku == '') return;
@@ -317,7 +316,7 @@ function closePopup() {
 function changeItem(item) {
     item.contact = item.name;
     item.serviceenterprisename = item.serviceenterprisename || item.enterprisename;
-    ['contact', 'serviceenterprisename', 'sku', 'cardno', 'itemid', 'itemname', 'itemno', 'model', 'phonenumber', 'unitname', 'spec'].forEach(key => {
+    ['contact', 'serviceenterprisename', 'sku', 'cardno', 'itemid', 'itemname', 'itemno', 'model', 'phonenumber', 'unitname', 'spec', 'sys_enterpriseid'].forEach(key => {
         form[key] = item[key] || '';
     });
     uni.showToast({ title: '已填充表单', icon: 'none' });
@@ -341,9 +340,7 @@ function openScan() {
                     const result = await codeReader.decodeFromImageElement(img);
                     form.sku = result.text;
                     skuConfirm();
-                    uni.showToast({ title: `识别成功: ${result.text}`, icon: 'none' });
                 } catch (err) {
-                    console.log("未识别出二维码或条形码");
                     uni.showToast({ title: '未识别出二维码或条码', icon: 'none' });
                 }
             };
@@ -370,7 +367,7 @@ function toSelectProduct2() {
         url: '/pages/select/myProduct'
     });
     $Http.onSelected = (item) => {
-        ['contact', 'phonenumber', 'sku', 'serviceenterprisename', 'cardno', 'itemid', 'itemname', 'itemno', 'model', 'unitname', 'spec', 'address', 'province', 'city', 'county'].forEach(key => {
+        ['contact', 'phonenumber', 'sku', 'serviceenterprisename', 'cardno', 'itemid', 'itemname', 'itemno', 'model', 'unitname', 'spec', 'address', 'province', 'city', 'county', 'sys_enterpriseid'].forEach(key => {
             form[key] = item[key] || '';
         });
         uni.navigateBack()

+ 2 - 2
pages/index/home.vue

@@ -15,7 +15,7 @@
 						故障维修排除
 					</view>
 				</navigator>
-				<view class="item" hover-class="navigator-hover">
+				<navigator class="item" url="/pages/serviceSeeking/index">
 					<view class="icon">
 						<image class="image" src="/static/image/qt.png" />
 					</view>
@@ -25,7 +25,7 @@
 					<view class="text">
 						其他问题服务
 					</view>
-				</view>
+				</navigator>
 			</view>
 		</view>
 	</view>

+ 4 - 1
pages/select/accessoriesList.vue

@@ -16,7 +16,7 @@
                     分类:{{ item.bomfullname || '--' }}
                 </view>
                 <view class="row">
-                    售价:<text class="price">{{ item.price }}</text>
+                    售价:<text class="price">{{ $Http.formatNumber(item.price) }}</text>
                 </view>
             </view>
         </view>
@@ -27,6 +27,9 @@
 import { defineProps, defineEmits } from 'vue';
 const emit = defineEmits(['uploadCallback'])
 
+import { getCurrentInstance } from 'vue';
+const { $Http } = getCurrentInstance().proxy;
+
 const props = defineProps({
     list: {
         type: Array

+ 483 - 0
pages/serviceSeeking/detail.vue

@@ -0,0 +1,483 @@
+<template>
+    <view v-if="time">
+        <view style="background-color: #fff;padding: 20rpx 0;">
+            <up-steps :current="current" dot activeColor="#0279FE">
+                <up-steps-item v-for="item in steps" :key="item.title" :title="item.title" />
+            </up-steps>
+        </view>
+        <view class="main">
+            <view class="billno" style="margin-bottom: -24rpx;">
+                工单编号:{{ detail.billno }}
+            </view>
+            <view class="row">
+                <view class="label col-center">服务类型</view>
+                <view class="servicetype"
+                    :style="{ 'background': { '安装': '#70B603', '维修': '#D9001B', '清洁': '#3874F6', '清洗': '#3874F6' }[detail.servicetype] || '#999999' }">
+                    {{ detail.servicetype || '--' }}</view>
+            </view>
+            <view class="row">
+                <view class="label">产品品类</view>
+                <view class="value">{{ detail.class1 || '--' }}</view>
+            </view>
+            <view class="row" v-if="detail.servicetype == '维修'">
+                <view class="label">故障类型</view>
+                <view class="value">{{ detail.class2 || '--' }}</view>
+            </view>
+            <view class="row">
+                <view class="label">服务地址</view>
+                <view class="value">{{ detail.province + detail.city + detail.county + detail.address || '--' }}</view>
+            </view>
+            <view class="row" style="align-items: center;">
+                <view class="label justify">联系人</view>
+                <view class="value phonenumber">{{ detail.scenecontact || '--' }}
+                    <block v-if="detail.scenecontactphonenumber">
+                        <text style="margin: 0 20rpx 0 10rpx;">
+                            {{ detail.scenecontactphonenumber }}
+                        </text>
+                        <My-button :customStyle="{
+                            width: '142rpx',
+                            height: '48rpx',
+                            'background-color': '#FFFFFF',
+                            'color': '#3874F6',
+                            borderRadius: '10rpx'
+                        }" :frontIconStyle="{
+                            marginRight: '6rpx',
+                        }" frontIcon="icon-bodadianhua1" text="电话" :phonenumber="detail.scenecontactphonenumber" />
+                    </block>
+                </view>
+            </view>
+            <view class="row">
+                <view class="label">派单日期</view>
+                <view class="value">{{ detail.createdate || '--' }}</view>
+            </view>
+            <view class="row">
+                <view class="label">服务需求</view>
+                <view class="value">{{ detail.remarks || '--' }}</view>
+            </view>
+            <up-transition :show="transition">
+                <view class="transition">
+                    <up-divider />
+                    <view class="row" v-if="detail.enterprisename">
+                        <view class="label justify">销售商</view>
+                        <view class="value">{{ detail.enterprisename || '--' }}</view>
+                    </view>
+                    <view class="row" v-if="detail.itemsText.length">
+                        <view class="label">产品信息</view>
+                        <view class="value">
+                            <view class="col-center" v-for="(item, index) in detail.itemsText" :key="index">
+                                <view class="value"><text v-if="detail.itemsText.length != 1">{{ index + 1 }}. </text>{{
+                                    item }}
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+                    <view class="row" v-if="detail.sku">
+                        <view class="label justify">序列号</view>
+                        <view class="value">{{ detail.sku || '--' }}</view>
+                    </view>
+                    <view class="row" v-if="detail.customername || detail.customerphonenumber"
+                        style="align-items: center;">
+                        <view class="label justify">客户信息</view>
+                        <view class="value phonenumber">{{ detail.customername || '' }}
+                            <block v-if="detail.customerphonenumber">
+                                <text style="margin: 0 20rpx 0 10rpx;">
+                                    {{ detail.customerphonenumber }}
+                                </text>
+                                <My-button :customStyle="{
+                                    width: '142rpx',
+                                    height: '48rpx',
+                                    'background-color': '#FFFFFF',
+                                    'color': '#3874F6',
+                                    borderRadius: '10rpx'
+                                }" frontIcon="icon-bodadianhua1" text="电话" :phonenumber="detail.customerphonenumber" />
+                            </block>
+                        </view>
+                    </view>
+                    <view class="row" v-if="detail.servicetype !== '安装' && detail.cardno">
+                        <view class="label">保修信息</view>
+                        <view class="value">
+                            <text :style="{ color: detail.inqualityguaranteeperiod ? '#70B603' : '#D9001B' }"
+                                style="margin-right: 0rpx;">
+                                {{ detail.inqualityguaranteeperiod ? '在保' : '已过保' }}
+                            </text>
+                            {{ detail.cardno || '' }}
+                            <view v-if="detail.cardno && detail.cardbegdate" style="margin-top: 20rpx;">
+                                {{ detail.cardbegdate || '' }} - {{ detail.cardenddate || '' }}
+                            </view>
+                        </view>
+                    </view>
+                </view>
+            </up-transition>
+            <block v-if="detail.status == '进行中'">
+                <view style="height: 40rpx;" />
+                <view class="changeTransition" hover-class="navigator-hover" @click="transition = !transition">
+                    详细信息
+                    <view class="iconfont icon-dianjizhankai" :class="transition ? 'shrink' : ''" />
+                </view>
+            </block>
+            <!-- 底部按钮 -->
+            <view v-if="detail.status == '待接单'" class="but-box">
+                <view class="but-box-item" v-if="detail.customerphonenumber">
+                    <My-button :customStyle="{
+                        'background-color': '#FFFFFF',
+                        'color': '#3874F6',
+                    }" frontIcon="icon-bodadianhua1" text="电话" :phonenumber="detail.customerphonenumber" />
+                </view>
+                <view class="but-box-item" @click="takeOrderShow = true">
+                    <My-button frontIcon="icon-jiedan" text="接单" />
+                    <up-modal negativeTop="100" :show="takeOrderShow" title="是否确认接单?" showCancelButton
+                        @confirm="takeOrders" @cancel="takeOrderShow = false" ref="uModal"
+                        :asyncClose="true"></up-modal>
+                </view>
+            </view>
+            <!-- 底部按钮 -->
+            <view v-else-if="detail.status == '待开始'" class="but-box">
+                <view class="but-box-item" @click="toChangeMsg">
+                    <My-button :customStyle="{
+                        'background-color': '#FFFFFF',
+                        'color': '#3874F6',
+                    }" text="信息修改" />
+                </view>
+                <view class="but-box-item" @click="!detail.sku ? toCMTips() : confirmStartShow = true">
+                    <My-button text="确认开始" />
+                    <up-modal negativeTop="100" :show="confirmStartShow" showCancelButton @confirm="confirmStart"
+                        @cancel="confirmStartShow = false" ref="uModal" :asyncClose="true">
+                        <view class="slot-content">
+                            <view>
+                                请仔细核对服务信息和客户信息
+                            </view>
+                            <view style="margin-top: 20rpx;">
+                                开始工单后确认信息将不可修改!
+                            </view>
+                        </view>
+                    </up-modal>
+                </view>
+            </view>
+        </view>
+
+        <view class="main" v-if="current >= 2 && detail.nodes.length" style="padding: 30rpx;">
+            <nodes ref="Nodes" :nodes="detail.nodes" />
+            <view v-if="detail.status == '进行中'" style='width: 60%;margin: 40rpx auto 0;padding-bottom: 30rpx;'
+                @click="submit"><My-button text="完工提交" />
+            </view>
+        </view>
+
+    </view>
+
+    <view style="height: 50px;" />
+</template>
+
+<script setup>
+import { ref, reactive, getCurrentInstance } from 'vue';
+const { $Http } = getCurrentInstance().proxy;
+import { onLoad, onShow, onUnload } from '@dcloudio/uni-app';
+import nodes from './modules/nodes.vue';
+
+const transition = ref(true);
+const Nodes = ref(null);
+
+const current = ref(-1);
+const steps = reactive([
+    { title: '接单', value: '待接单' },
+    { title: '开始', value: '待开始' },
+    { title: '进行中', value: '进行中' },
+    { title: '提交', value: '提交' },
+    { title: '完工', value: '已完成' }
+]);
+let sa_workorderid = 0;
+onLoad((options) => {
+    sa_workorderid = options.id;
+});
+
+onUnload(() => {
+    delete $Http.content1
+});
+
+const time = ref(new Date().getTime());
+
+onShow(() => {
+    getDetail()
+})
+
+let detail = reactive({
+    itemsText: [],
+    servicetype: ""
+});
+
+function getDetail() {
+    $Http.basic({ "id": "20230208140103", "content": { "nocache": true, sa_workorderid } }).then(res => {
+        console.log("工单详情", res)
+        $Http.content1 = {
+            itemid: res.data.itemid,
+            sku: res.data.sku,
+            sys_enterpriseid: res.data.sys_enterpriseid,
+            sa_workorderid: res.data.sa_workorderid,
+        }
+        if (res.code !== 1) return uni.showToast({ title: res.msg, icon: 'none' });
+        try {
+            res.data.itemsText = res.data.titems.map(item => {
+                return `${item.itemname} ${item.model}`;
+            })
+        } catch (error) {
+
+        }
+
+        if (res.data.status == '进行中' && detail.servicetype == '') transition.value = false;
+        time.value = new Date().getTime()
+        detail = reactive(res.data);
+        current.value = steps.findIndex(item => item.value === res.data.status);
+    })
+}
+
+// 提交工单
+function submit() {
+    // 收集所有必填末级节点
+    const requiredLeafNodes = [];
+
+    // 递归查找末级节点
+    function findLeafNodes(nodeList) {
+        for (const node of nodeList) {
+            // 检查是否为末级节点(没有子节点)
+            if (!node.child || node.child.length === 0) {
+                // 检查是否是必填节点(required == 11)
+                if (node.workpresetjson?.required == 11 && node.status !== "1") {
+                    requiredLeafNodes.push(node);
+                }
+            } else {
+                // 递归检查子节点
+                findLeafNodes(node.child);
+            }
+        }
+    }
+    // 开始查找
+    findLeafNodes(detail.nodes);
+    // 检查所有必填末级节点是否已完成
+    const allCompleted = requiredLeafNodes.every(node => node.status === "1");
+    if (allCompleted) {
+        uni.showModal({
+            confirmText: '继续提交',
+            content: '请确认工单内容,提交后不可修改!',
+            success: ({ confirm }) => {
+                if (confirm) {
+                    $Http.basic({
+                        id: 2025072315401603,
+                        "content": {
+                            "sa_workorderid": detail.sa_workorderid
+                        }
+                    }).then(res => {
+                        console.log("提交结果", res)
+                        uni.showToast({
+                            title: res.code == 1 ? '提交成功' : res.msg,
+                            icon: 'none'
+                        });
+                        if (res.code == 1) {
+                            getDetail();
+                            transition.value = true;
+                        }
+                    })
+                }
+            },
+        })
+    } else {
+        uni.showModal({
+            confirmText: '关闭',
+            content: '还有工序未完成,不能提交!',
+            showCancel: false
+        })
+    }
+}
+
+
+// 接单
+const takeOrderShow = ref(false);
+
+function takeOrders() {
+    $Http.basic({
+        id: 20230210101103,
+        "content": {
+            "sa_workorderid": detail.sa_workorderid
+        }
+    }).then(res => {
+        console.log("接单结果", res)
+        if (res.code == 1) {
+            takeOrderShow.value = false;
+            getDetail();
+            uni.showToast({
+                title: '接单成功',
+                icon: 'none'
+            });
+        } else {
+            if (res.msg) uni.showToast({
+                title: res.msg,
+                icon: 'none'
+            });
+        }
+    })
+}
+
+function toCMTips() {
+    uni.showModal({
+        confirmText: '前去修改',
+        content: '请完善产品信息和客户信息,确认开始后将不可修改!',
+        success: ({ confirm }) => {
+            if (confirm) toChangeMsg();
+        },
+    })
+}
+
+// 修改信息
+function toChangeMsg() {
+    uni.navigateTo({
+        url: `/pages/workOrder/changeMsg?id=` + sa_workorderid
+    });
+};
+
+// 确认开始
+const confirmStartShow = ref(false);
+
+function confirmStart() {
+    $Http.basic({
+        id: 20230209144503,
+        "content": {
+            "sa_workorderid": detail.sa_workorderid
+        }
+    }).then(res => {
+        console.log("确认开始", res)
+        if (res.code == 1) {
+            confirmStartShow.value = false;
+            getDetail();
+            uni.showToast({
+                title: '确认开始成功',
+                icon: 'none'
+            });
+        } else {
+            if (res.msg) uni.showToast({
+                title: res.msg,
+                icon: 'none'
+            });
+        }
+    })
+}
+
+defineExpose({ detail })
+
+</script>
+
+<style lang="scss" scoped>
+.main {
+    position: relative;
+    width: 690rpx;
+    background: #FFFFFF;
+    box-shadow: 0rpx 4rpx 16rpx 2rpx rgba(150, 157, 165, 0.16);
+    border-radius: 10rpx;
+    box-sizing: border-box;
+    padding: 40rpx;
+    margin: 40rpx auto 0;
+    overflow: hidden;
+
+    .changeTransition {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        color: #3874F6;
+        font-size: 28rpx;
+        position: absolute;
+        bottom: 0;
+        left: 0;
+        width: 100%;
+        height: 60rpx;
+        background: #EDF4FF;
+
+        .icon-dianjizhankai {
+            font-size: 20rpx;
+            margin-left: 16rpx;
+            transform: rotate(-90deg);
+            transition: transform 0.3s ease;
+        }
+
+        .shrink {
+            transform: rotate(90deg);
+        }
+    }
+
+    .row {
+        display: flex;
+        font-size: 28rpx;
+        font-family: Microsoft YaHei, Microsoft YaHei;
+        line-height: 32rpx;
+        margin-top: 20rpx;
+
+        .label {
+            position: relative;
+            width: 108rpx;
+            color: #999999;
+            box-sizing: border-box;
+            flex-shrink: 0;
+            margin-right: 30rpx;
+        }
+
+        .label::after {
+            position: absolute;
+            top: 0;
+            right: -12rpx;
+            content: ':';
+        }
+
+        .value {
+            color: #333333;
+        }
+
+        .phonenumber {
+            width: 100%;
+            display: flex;
+            align-items: center;
+        }
+
+        .servicetype {
+            border-radius: 8rpx;
+            padding: 8rpx 16rpx;
+            font-weight: bold;
+            color: #fff;
+        }
+    }
+
+    .col-center {
+        display: flex;
+        align-items: center;
+    }
+
+    .col-center::after {
+        top: 50% !important;
+        transform: translateY(-50%);
+    }
+
+    .justify {
+        text-align: justify;
+        text-align-last: justify;
+    }
+
+    .billno {
+        position: relative;
+        width: 400rpx;
+        top: -40rpx;
+        left: -40rpx;
+        background: #3874F6;
+        border-radius: 0 0rpx 24rpx 0rpx;
+        padding: 10rpx 52rpx 10rpx 20rpx;
+        font-family: Microsoft YaHei, Microsoft YaHei;
+        font-size: 26rpx;
+        color: #FFFFFF;
+    }
+}
+
+.but-box {
+    display: flex;
+    align-items: center;
+    justify-content: space-around;
+    margin-top: 40rpx;
+
+    .but-box-item {
+        width: 45%;
+    }
+}
+</style>

+ 172 - 0
pages/serviceSeeking/index.vue

@@ -0,0 +1,172 @@
+<template>
+    <view v-if="!['待接单', '已完成'].includes(content.where.status)" style="background-color: #fff;">
+        <up-tabs lineColor="#3874F6" :scrollable="false" :list="[{
+            name: '安装'
+        }, {
+            name: '维修'
+        }, {
+            name: '清洗'
+        }]" @click="tabClick" />
+    </view>
+    <My_listbox ref="listBox" :empty="!list.length" @getlist="getList">
+        <view style="height: 18rpx;" />
+        <navigator class="item" v-for="item in list" :key="item.sa_serviceorderid"
+            :url="'/pages/serviceSeeking/detail?id=' + item.sa_serviceorderid" hover-class="navigator-hover">
+            <view class="head">
+                <view class="tag"
+                    :style="{ 'background': { '已完结': '#333', '拒绝受理': '#333' }[item.status] || '#3874F6' }">
+                    {{ item.status }}
+                </view>
+                <view class="time">
+                    {{ item.createdate }}
+                </view>
+            </view>
+            <view class="address">
+                {{ item.address }}
+            </view>
+            <view class="user">
+                {{ item.serviceenterprisename + ' ' + item.servicephonenumber }}
+            </view>
+            <view class="but-box" @click.stop>
+                <view class="but-box-item" v-if="item.servicephonenumber">
+                    <My-button :customStyle="{
+                        'background-color': '#FFFFFF',
+                        'color': '#3874F6',
+                        height: '70rpx',
+                    }" frontIcon="icon-bodadianhua1" text="电话" :phonenumber="item.servicephonenumber" />
+                </view>
+
+                <navigator :url="'/pages/workOrder/detail?id=' + item.sa_serviceorderid" class="but-box-item">
+                    <My-button :customStyle="{
+                        height: '70rpx',
+                    }" frontIcon="icon-jiedan" text="查询" />
+                </navigator>
+            </view>
+        </navigator>
+    </My_listbox>
+</template>
+
+<script setup>
+import { ref, reactive, getCurrentInstance } from 'vue';
+const { $Http } = getCurrentInstance().proxy;
+import { onLoad, onShow } from '@dcloudio/uni-app';
+
+const listBox = ref(null);
+const content = reactive({
+    loading: false,
+    isadmin: 0,
+    "pageNumber": 1,
+    "pageSize": 20,
+    "where": {
+        "servicetype": '安装',
+    }
+});
+const list = ref([])
+
+function tabClick(e) {
+    content.where.servicetype = e.value || e.name;
+    getList(true);
+}
+
+onLoad(() => {
+    getList();
+});
+
+onShow(() => {
+    if (content.pageNumber != 1) $Http.updateList(content, getList)
+})
+
+function getList(init = false) {
+    if (content.loading) return;
+    if (init) content.pageNumber = 1;
+    content.loading = true;
+    console.log("uni.getStorageSync('userRecord')", uni.getStorageSync('userRecord'))
+    try {
+        let sa_customersid = uni.getStorageSync('userRecord').sa_customersid;
+        if (!sa_customersid || sa_customersid < 0) {
+            content.where.sa_customersid = uni.getStorageSync('userRecord').sa_customersid;
+        } else {
+            content.where.customerphonenumber = uni.getStorageSync('phonenumber') || ''
+        }
+    } catch (error) {
+        content.where.customerphonenumber = uni.getStorageSync('phonenumber') || ''
+    }
+    $Http.basic({
+        "id": "20230206091703",
+        content
+    }).then(res => {
+        console.log("获取列表", res)
+        content.loading = false;
+        listBox.value.refreshToComplete();
+        listBox.value.setHeight();
+        if (res.code == 1) {
+            list.value = reactive(res.firstPage ? res.data : list.value.concat(res.data));
+            content.pageTotal = res.pageTotal;
+            content.pageNumber = res.pageNumber;
+        } else {
+            if (res.msg) uni.showToast({
+                title: res.msg,
+                icon: 'none'
+            });
+        }
+    })
+}
+
+</script>
+
+<style lang="scss" scoped>
+.item {
+    width: 690rpx;
+    background: #FFFFFF;
+    box-shadow: 0rpx 4rpx 16rpx 2rpx rgba(150, 157, 165, 0.16);
+    margin: 0 auto 20rpx;
+    padding: 20rpx 40rpx;
+    box-sizing: border-box;
+
+    .head {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+
+        .tag {
+            font-family: PingFang SC, PingFang SC;
+            font-size: 24rpx;
+            color: #FFFFFF;
+            padding: 8rpx 20rpx;
+        }
+
+        .time {
+            font-family: Microsoft YaHei, Microsoft YaHei;
+            font-size: 24rpx;
+            color: #999999;
+        }
+    }
+
+    .address {
+        line-height: 38rpx;
+        font-family: Microsoft YaHei, Microsoft YaHei;
+        font-size: 28rpx;
+        color: #333333;
+        margin-top: 20rpx;
+    }
+
+    .user {
+        line-height: 32rpx;
+        font-family: Microsoft YaHei, Microsoft YaHei;
+        font-size: 24rpx;
+        color: #999999;
+        margin-top: 20rpx;
+    }
+
+    .but-box {
+        display: flex;
+        align-items: center;
+        justify-content: space-around;
+        margin-top: 20rpx;
+
+        .but-box-item {
+            width: 40%;
+        }
+    }
+}
+</style>

+ 128 - 0
pages/serviceSeeking/modules/nodes.vue

@@ -0,0 +1,128 @@
+<template>
+    <view class="node" :class="prefix ? 'child' : ''"
+        :hover-class="!prefix && item.child.length ? '' : 'navigator-hover'" v-for="item in nodes"
+        :key="item.sa_workorder_nodeid" @click.stop="toWork(item)">
+        <view class="number">
+            {{ (prefix ? prefix + '-' : '') + item.rowindex }}.
+        </view>
+        <view style="flex: 1;">
+            <view class="label">
+                {{ item.workpresetjson.workname }}
+            </view>
+            <node v-if="!prefix && item.child.length" :nodes="item.child"
+                :prefix="(prefix ? prefix + '-' : '') + item.rowindex" />
+
+            <block v-else>
+                <view class="images" v-if="item.attinfos.length">
+                    <view class="image" hover-class="navigator-hover" v-for="(img, index) in item.attinfos"
+                        :key="img.attachmentid" @click.stop="preview(index, item.attinfos)">
+                        <up-image :show-loading="true" :src="$Http.getSpecifiedImage(img)" width="80px" height="80px" />
+                    </view>
+                </view>
+                <view class="stuta">
+                    <view class="tag" style="background-color: #E64D55;" v-if="item.status == '0'">未完成</view>
+                    <view class="tag" style="background-color: #06A971;" v-if="item.status == '1'">已完成</view>
+                    <view class="tag" style="background-color: #70B603;" v-if="item.status == '2'">进行中</view>
+                </view>
+            </block>
+        </view>
+    </view>
+</template>
+
+<script setup>
+import { getCurrentInstance } from 'vue';
+const { $Http } = getCurrentInstance().proxy;
+import { defineProps } from 'vue';
+import node from "./nodes.vue";
+
+const props = defineProps({
+    nodes: {
+        type: Array
+    },
+    prefix: {
+        type: [String, Number],
+    }
+});
+function toWork(item) {
+    try {
+        if (item.child.length) return;
+    } catch (error) {
+        item.child = [];
+    }
+    if (item.child.length) return;
+    item.title = (props.prefix ? props.prefix + '-' : '') + item.rowindex + '. ' + item.workpresetjson.workname;
+    $Http.data = item;
+
+    uni.navigateTo({
+        url: '/pages/workOrder/work',
+    })
+}
+
+function preview(index, imgs) {
+    wx.previewImage({
+        urls: imgs.filter(v => isImage(v.postfix)).map(item => item.url),
+        current: imgs[index].url,
+
+    })
+}
+function isImage(postfix) {
+    const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
+    return imageTypes.includes(postfix.toLowerCase());
+}
+
+
+</script>
+
+<style lang="scss" scoped>
+.node {
+    display: flex;
+    padding: 10rpx;
+    margin-top: 10rpx;
+    font-family: Microsoft YaHei, Microsoft YaHei;
+    border-radius: 8rpx;
+
+    .label,
+    .number {
+        font-size: 32rpx;
+    }
+
+    .number {
+        margin-right: 10rpx;
+    }
+
+    .stuta {
+        margin-top: 20rpx;
+        display: flex;
+
+        .tag {
+            border-radius: 8rpx;
+            padding: 8rpx 12rpx;
+            font-size: 24rpx;
+            color: #fff;
+        }
+    }
+
+    .images {
+        display: flex;
+        flex-wrap: wrap;
+
+        .image {
+            margin-top: 20rpx;
+            margin-right: 20rpx;
+        }
+    }
+}
+
+
+.child {
+    margin-left: -55rpx;
+    width: calc(100% + 65rpx);
+    transform: scale(0.9);
+
+    .label,
+    .number {
+        color: #333;
+    }
+
+}
+</style>

+ 140 - 0
pages/serviceSeeking/modules/products.vue

@@ -0,0 +1,140 @@
+<template>
+    <up-swipe-action>
+        <up-swipe-action-item :disabled="disabled" v-for="item in list" :key="item.itemid" :options="options1"
+            @click="onClick(item)">
+            <view class="item">
+                <view class="left" @click.stop="previewImge(item.imageUrl)">
+                    <up-image :show-loading="true" :src="item.imageUrl" width="100%" height="100%" />
+                </view>
+                <view class="right">
+                    <view class="itemname">
+                        {{ item.itemname || '--' }}
+                    </view>
+                    <view class="row">
+                        分类:{{ item.bomfullname || '--' }}
+                    </view>
+                    <view class="row">
+                        型号:{{ item.model || '--' }}
+                    </view>
+                    <view class="row">
+                        售价:<text class="price">{{ $Http.formatNumber(item.price) }}</text>
+                    </view>
+                    <view class="number">
+                        <up-number-box :disabled="disabled" v-model="item.qty" @change="valChange" />
+                    </view>
+                </view>
+            </view>
+        </up-swipe-action-item>
+    </up-swipe-action>
+</template>
+
+<script setup>
+import { defineProps, defineEmits, ref, reactive } from 'vue';
+import { getCurrentInstance } from 'vue';
+const { $Http } = getCurrentInstance().proxy;
+const emits = defineEmits(['deleteItem']);
+const props = defineProps({
+    list: {
+        type: Array
+    },
+    deleteItem: {
+        type: Function
+    },
+    disabled: {
+        type: Boolean,
+        default: false
+    }
+});
+
+// 使用 reactive 创建响应式对象  
+const options1 = reactive([{
+    text: '删除',
+    style: {
+        backgroundColor: '#FA5151'
+    }
+}]);
+
+function onClick(item) {
+    uni.showModal({
+        title: '提示',
+        content: `是否确定删除“${item.itemname || '该产品'}”?`,
+        confirmText: '删除',
+        success: ({ confirm }) => {
+            if (confirm) {
+                emits('deleteItem', item);
+            }
+        },
+    })
+}
+
+function valChange(e) {
+    console.log(e)
+}
+
+function previewImge(url) {
+    if (!url) return;
+    uni.previewImage({
+        urls: [url],
+        current: url
+    });
+}
+</script>
+
+<style lang="scss" scoped>
+.item {
+    display: flex;
+    width: 100%;
+    background: #FFFFFF;
+    border-radius: 20rpx;
+    padding: 16rpx;
+    padding-left: 0;
+    box-sizing: border-box;
+    margin-top: 16rpx;
+
+    .left {
+        flex-shrink: 0;
+        width: 174rpx;
+        height: 174rpx;
+        background: #FFFFFF;
+        border-radius: 8rpx;
+        border: 2rpx solid #707070;
+        margin-right: 20rpx;
+        overflow: hidden;
+    }
+
+    .right {
+        position: relative;
+        flex: 1;
+
+        .itemname {
+            line-height: 38rpx;
+            font-family: PingFang SC, PingFang SC;
+            font-weight: bold;
+            font-size: 28rpx;
+            color: #333333;
+        }
+
+        .row {
+            line-height: 40rpx;
+            font-family: PingFang SC, PingFang SC;
+            font-size: 24rpx;
+            color: #999999;
+
+            .price {
+                color: #FA5151;
+            }
+        }
+
+        .number {
+            position: absolute;
+            right: 0rpx;
+            bottom: 0rpx;
+        }
+
+    }
+}
+
+.item:first-child {
+    margin-top: 0;
+}
+</style>

+ 8 - 0
utils/Http.js

@@ -16,6 +16,14 @@ class HTTP {
       content.pageNumber = 1;
       getList();
     }
+
+    this.formatNumber = (num, decimalPlaces = 2) => {
+      if (!num && num !== 0) return '';
+      let [integer, decimal] = String(num.toFixed(decimalPlaces)).split('.');
+      integer = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+      return decimal ? `${integer}.${decimal}` : integer;
+    }
+
     this.getSpecifiedImage = function (obj, getType = false) {
       try {
         let type = getType ? "compressed" : "thumbnail";