Browse Source

fix: 新增省区市控件,引入省市区数据源依赖、工作台样式优化、搜索页面布局完善

lirenjie 4 months ago
parent
commit
f536f0e7fe

+ 1 - 0
package.json

@@ -62,6 +62,7 @@
     "@dcloudio/uni-mp-xhs": "3.0.0-3090920231225001",
     "@dcloudio/uni-quickapp-webview": "3.0.0-3090920231225001",
     "@dcloudio/uni-ui": "1.5.0",
+    "china-area-data": "^5.0.1",
     "crypto-js": "4.1.1",
     "dayjs": "1.11.10",
     "lodash": "4.17.21",

+ 102 - 0
src/components/aera-picker/index.vue

@@ -0,0 +1,102 @@
+<template>
+    <view>
+        <view>{{ address }}</view>
+        <u-picker :show="showPicker" :inputProps="{ placeholder: '请选择省市区' ,border:'none'}" :columns="areaList" keyName="label"
+            valueName="value" hasInput v-model="selectedValues" @change="changeHandler" ref="uPickerRef"
+            @confirm="handleConfirm" @cancel="showPicker = false">
+        </u-picker>
+    </view>
+</template>
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import ChinaAreaData from 'china-area-data/v5/data.json'
+const props = defineProps({
+    modelValue: {
+        type: Array,
+        default: () => []
+    }
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+const showPicker = ref(false)
+const selectedValues = ref(props.modelValue)
+const address = ref(null)
+const areaList = ref([
+    [], [], []
+])
+function transformData(data) {
+    const result = [];
+
+    // First, process the provinces (level 1)
+    const provinces = data['86'];
+    for (const [provinceCode, provinceName] of Object.entries(provinces)) {
+        const province = {
+            label: provinceName,
+            value: provinceCode,
+            children: []
+        };
+
+        // Then process the cities (level 2)
+        const cities = data[provinceCode];
+        if (cities) {
+            for (const [cityCode, cityName] of Object.entries(cities)) {
+                const city = {
+                    label: cityName,
+                    value: cityCode,
+                    children: []
+                };
+
+                // Finally process the districts (level 3)
+                const districts = data[cityCode];
+                if (districts) {
+                    for (const [districtCode, districtName] of Object.entries(districts)) {
+                        city.children.push({
+                            label: districtName,
+                            value: districtCode
+                        });
+                    }
+                }
+                province.children.push(city);
+            }
+        }
+
+        result.push(province);
+    }
+    console.log(result);
+    return result;
+
+}
+function setAreaList() {
+    const province = transformData(ChinaAreaData)
+    // area.value 为后端接口获取到的数据
+    areaList.value[0].push(...province) // 省
+    areaList.value[1].push(...province[0].children) // 市
+    areaList.value[2].push(...province[0].children[0].children) // 区
+    console.log(areaList.value);
+    
+}
+onMounted(() => {
+    setAreaList()
+})
+const uPickerRef = ref(null)
+// change事件 当选择值变化时触发	
+const changeHandler = (e) => {
+    const { columnIndex, value } = e
+    let city = value[0].children
+    if (columnIndex === 0) { // 省变化更新市、区列表
+        // setColumnValues(index, setVal) 设置对应列的选择值
+        uPickerRef.value.setColumnValues(1, city);
+        uPickerRef.value.setColumnValues(2, city[0].children);
+    } else if (columnIndex === 1) { // 市变化更新区/县列表
+        uPickerRef.value.setColumnValues(2, value[1].children);
+    }
+}
+// 确认选择
+const handleConfirm = (e) => {
+    selectedValues.value = e.value
+    emit('update:modelValue', e.value)
+    showPicker.value = false
+}
+</script>
+<style lang='scss' scoped></style>

+ 185 - 0
src/pages-workbench/customer-manage/create-customer/index.vue

@@ -0,0 +1,185 @@
+<template>
+    <view class="create-customer">
+        <up-form labelPosition="left" :model="formData" :rules="rules" ref="form1" labelWidth="100">
+            <up-form-item label="客户名称" prop="name" :borderBottom="true" required>
+                <up-input v-model="formData.name" placeholder="请输入客户名称">
+                    <template #suffix>
+                        <up-button type="primary">天眼查</up-button>
+                    </template>
+                </up-input>
+            </up-form-item>
+            <up-form-item label="客户信息" prop="customerType" :borderBottom="true" required>
+                <up-radio-group v-model="formData.customerType" placement="row" @change="groupChange">
+                    <up-radio :customStyle="{ marginBottom: '8px' }" v-for="(item, index) in radiolist" :key="index"
+                        :label="item.name" :name="item.name" @change="radioChange">
+                    </up-radio>
+                </up-radio-group>
+            </up-form-item>
+            <up-form-item label="客户来源" prop="customerType" :borderBottom="true" required>
+                <up-picker-data v-model="formData.customerType" title="请选择客户来源" :options="cateList" valueKey="id"
+                    labelKey="name">
+                </up-picker-data>
+                <template #right>
+                    <up-icon name="arrow-right"></up-icon>
+                </template>
+            </up-form-item>
+            <up-form-item label="服务商名称" prop="customerType" :borderBottom="true" required>
+                <up-picker-data v-model="formData.customerType" title="请选择服务商" :options="cateList" valueKey="id"
+                    labelKey="name">
+                </up-picker-data>
+                <template #right>
+                    <up-icon name="arrow-right"></up-icon>
+                </template>
+            </up-form-item>
+            <up-form-item label="企业属性" prop="customerType" :borderBottom="true" required>
+                <up-picker-data v-model="formData.customerType" title="请选择企业属性" :options="cateList" valueKey="id"
+                    labelKey="name">
+                </up-picker-data>
+                <template #right>
+                    <up-icon name="arrow-right"></up-icon>
+                </template>
+            </up-form-item>
+            <up-form-item label="社会代码" prop="customerType" :borderBottom="true" required>
+                <up-input v-model="formData.name" border="none" placeholder="请输入统一社会代码"></up-input>
+            </up-form-item>
+            <up-form-item label="法定代表人" prop="customerType" :borderBottom="true" required>
+                <up-input v-model="formData.name" border="none" placeholder="请输入法定代表人"></up-input>
+            </up-form-item>
+            <up-form-item label="存续状态" prop="customerType" :borderBottom="true" required>
+                <up-picker-data v-model="formData.customerType" title="请选择企业属性" :options="cateList" valueKey="id"
+                    labelKey="name">
+                </up-picker-data>
+                <template #right>
+                    <up-icon name="arrow-right"></up-icon>
+                </template>
+            </up-form-item>
+            <up-form-item label="注册地址" prop="customerType" :borderBottom="true" required>
+                <AreaPicker v-model="formData.customerType"></AreaPicker>
+            </up-form-item>
+        </up-form>
+        <view class="action-button-group">
+            <up-button @click="reset" class="action-reset">重置</up-button>
+            <up-button @click="save" type="primary" plain class="action-button">保存</up-button>
+            <up-button @click="submit" type="primary" class="action-sumbit">提交</up-button>
+        </view>
+    </view>
+</template>
+<script setup>
+import { ref } from 'vue'
+import AreaPicker from '@/components/aera-picker/index.vue'
+const cateList = ref([
+    {
+        'id': 1,
+        'name': '分类1'
+    },
+    {
+        'id': 2,
+        'name': '分类2'
+    }
+
+])
+const radiolist = [
+    {
+        name: '国内客户'
+    },
+    {
+        name: '国外客户'
+    }
+]
+// 表单初始值
+const formData = ref({
+    name: '天眼查',
+    customerType: 'domestic',
+    source: '',
+    address: '',
+    detailAddress: '',
+    custom: '77777',
+    registerDate: ''
+})
+
+// 验证规则
+const rules = ref({
+    name: [
+        { required: true, message: '请输入客户名称', trigger: 'blur' }
+    ],
+    customerType: [
+        { required: true, message: '请选择客户类型', trigger: 'change' }
+    ]
+})
+
+// 表单变化事件
+const handleFormChange = ({ prop, value }) => {
+    console.log(`字段 ${prop} 变更为:`, value)
+}
+
+// 操作按钮事件
+const handleReset = () => {
+    formRef.value.resetFields()
+}
+
+const handleSave = async () => {
+    try {
+        const valid = await formRef.value.validate()
+        if (valid) {
+            uni.showToast({
+                title: '保存成功',
+                icon: 'success'
+            })
+        }
+    } catch (error) {
+        console.error('表单验证失败:', error)
+    }
+}
+
+const handleCreate = () => {
+    // 创建逻辑
+}
+</script>
+<style lang='scss' scoped>
+.create-customer {
+    background-color: #fff;
+    padding: 20rpx;
+
+    .cell {
+        :deep(.u-cell) {
+            width: 100%;
+            padding: 0;
+        }
+
+        .cell-value {
+            width: 100%;
+            padding: 0;
+        }
+    }
+
+}
+
+.action-button-group {
+    background-color: #fff;
+    width: 100%;
+    box-shadow: 0px -4rpx 20rpx 0px rgba(0, 0, 0, 0.25);
+    display: flex;
+    flex-direction: row;
+    position: fixed;
+    bottom: 0;
+    gap: 24rpx;
+
+    .action-reset {
+        flex: 1;
+        height: 64rpx;
+        font-size: 28rpx;
+    }
+
+    .action-button {
+        flex: 1;
+        height: 64rpx;
+        font-size: 28rpx;
+    }
+
+    .action-sumbit {
+        flex: 2;
+        height: 64rpx;
+        font-size: 28rpx;
+    }
+}
+</style>

+ 0 - 12
src/pages-workbench/customer-manage/index.vue

@@ -1,12 +0,0 @@
-<template>
-<view>
-
-</view>
-</template>
-<script setup>
-import { ref } from 'vue'
-
-</script>
-<style lang='scss' scoped>
-
-</style>

+ 2 - 2
src/pages.json

@@ -197,9 +197,9 @@
       "root": "pages-workbench",
       "pages": [
         {
-          "path": "customer-manage/index",
+          "path": "customer-manage/create-customer/index",
           "style": {
-            "navigationBarTitleText": "客户管理"
+            "navigationBarTitleText": "创建客户"
           }
         },
         {

+ 211 - 0
src/pages/common-search/index.vue

@@ -0,0 +1,211 @@
+<template>
+    <view class="search-page">
+        <view class="search-bar">
+            <up-icon name="arrow-left" @click="back"></up-icon>
+            <up-search v-model="keyword" :show-action="true" actionText="搜索" :animation="true" focus
+                :actionStyle="{ backgroundColor: '#2B69F8', color: '#FFFFFF' }" @search="search"></up-search>
+        </view>
+        <view class="search-history" v-if="historyRecord && historyRecord.length > 0">
+            <view class="search-history-header">
+                <view class="history-record">历史记录</view>
+                <view>
+                    <view v-if="deleteStatus" class="delete-status">
+                        <view class="delete-all" @click="deleteAll">全部删除</view>
+                        <view class="line"></view>
+                        <view class="finish" @click="switchDelete">完成</view>
+                    </view>
+                    <up-icon name="trash" v-else @click="switchDelete"></up-icon>
+                </view>
+            </view>
+            <view class="search-history-content">
+                <up-tag class="tag-item" v-for="(item, index) in historyRecord" shape="circle" :key="index"
+                    borderColor='#E9E9E9' :text="item.name" bgColor="#fff" color="#000" :name='index' padding="2px 8px"
+                    height="20px" borderRadius="16px" :closable="deleteStatus" @click="openHistory"
+                    @close="deleteTag"></up-tag>
+            </view>
+        </view>
+        <view class="search-result">
+            <view class="result-list" v-if="searchList && searchList.length > 0">
+
+            </view>
+            <view class="no-result-container" v-else>
+                <image class="img" src="@/static/images/common-search/noResult.png" mode="scaleToFill" />
+                <view class="no-result">内容溜走了,请重新搜索~</view>
+            </view>
+        </view>
+    </view>
+</template>
+<script setup>
+import { ref } from 'vue'
+import { onLoad } from '@dcloudio/uni-app'
+import { showConfirm, toast } from '../../utils/common';
+
+
+
+/* 历史记录 */
+const historyRecord = ref([
+    {
+        name: '科技园项目',
+        time: '2021-01-01 10:10:10',
+        uid: ''
+    },
+    {
+        name: '老旧小区改造项目',
+        time: '2021-01-01 10:10:10',
+        uid: ''
+    },
+    {
+        name: '工业园二期扩建',
+        time: '2021-01-01 10:10:10',
+        uid: ''
+    },
+    {
+        name: '安全复工通知',
+        time: '2021-01-01 10:10:10',
+        uid: ''
+    },
+])
+const deleteStatus = ref(false)
+function switchDelete() {
+    deleteStatus.value = !deleteStatus.value
+}
+function deleteTag(index) {
+    console.log(index);
+    showConfirm('确定删除这个历史记录吗?').then((res) => {
+        console.log(res);
+        historyRecord.value.splice(index, 1)
+        toast('删除成功')
+    })
+}
+function deleteAll() {
+    showConfirm('确定删除所有历史记录吗?').then((res) => {
+        console.log(res);
+        historyRecord.value = []
+        toast('删除成功')
+    })
+}
+function openHistory(index) {
+    console.log(index);
+    keyword.value = historyRecord.value[index].name
+    search(keyword.value)
+}
+/* 搜索 */
+const keyword = ref('')
+const searchList = ref([
+
+])
+async function search(keyword) {
+    console.log(keyword);
+    // await sleep(500)
+}
+/* 获取状态栏高度 */
+const statusBarHeight = ref(0)
+const url = ref('')
+const model = ref('')
+onLoad((query) => {
+    statusBarHeight.value = uni.getSystemInfoSync().statusBarHeight + 'px'
+    if (query) {
+        url.value = query.url
+        model.value = query.model
+    }
+    console.log(statusBarHeight.value);
+    console.log(query);
+})
+/* 返回 */
+function back() {
+    uni.navigateBack()
+}
+</script>
+<style lang='scss' scoped>
+.search-page {
+    margin-top: v-bind(statusBarHeight);
+
+    .search-bar {
+        display: flex;
+        flex-direction: row;
+        padding: 32rpx;
+    }
+
+    .search-history {
+        display: flex;
+        flex-direction: column;
+        background-color: #fff;
+        border-radius: 16rpx;
+        padding: 32rpx;
+        margin: 32rpx;
+        gap: 20rpx;
+
+        .search-history-header {
+            display: flex;
+            flex-direction: row;
+            justify-content: space-between;
+
+            .history-record {
+                font-weight: 500;
+                font-family: 'PingFang SC';
+                font-size: 32rpx;
+            }
+
+            .delete-status {
+                display: flex;
+                flex-direction: row;
+                align-items: center;
+                gap: 16rpx;
+
+                .delete-all {
+                    color: #797979;
+                }
+
+                .line {
+                    width: 2rpx;
+                    height: 16rpx;
+                    background-color: #797979;
+                }
+
+                .finish {}
+            }
+
+        }
+
+        .search-history-content {
+
+            display: flex;
+            flex-direction: row;
+            flex-wrap: wrap;
+            align-items: flex-start;
+            overflow-y: scroll;
+            gap: 16rpx;
+            width: 100%;
+            height: 220rpx;
+            word-break: break-all;
+        }
+    }
+
+    .search-result {
+        display: flex;
+        flex: 1;
+        height: 100%;
+
+        .result-list {}
+
+        .no-result-container {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            justify-content: center;
+            width: 100%;
+            height: 100%;
+
+            .img {
+                height: 92px;
+                width: 92px;
+            }
+
+            .no-result {
+                font-size: 28rpx;
+                font-family: 'PingFang SC';
+            }
+        }
+    }
+}
+</style>

+ 13 - 5
src/pages/tab-bar/workbench/customer-manage/index.vue

@@ -14,7 +14,7 @@
                 </u-dropdown>
             </view>
         </up-sticky>
-        <u-list height="440" style="padding-bottom: 16rpx;" scrollable enableBackToTop>
+        <u-list height="64vh" style="padding-bottom: 16rpx;" scrollable enableBackToTop>
             <u-list-item v-for="item in 6" :key="item">
                 <view class="u-list-item-content">
                     <view class="u-list-item-header">
@@ -50,7 +50,7 @@
             </u-list-item>
         </u-list>
     </view>
-    <view class="customer-float">
+    <view class="customer-float" @click="toCreateCustomer()">
         创建客户
     </view>
     <view class="customer-footer">
@@ -59,13 +59,13 @@
 </template>
 <script setup>
 import { ref } from 'vue'
-
 /* 搜索 */
 const keyword = ref('')
 const url = '/api/customer/list'
+const model = 'customer'
 function openSearch() {
     uni.navigateTo({
-        url: '/pages/common-search/index?url='+url
+        url: `/pages/common-search/index?url=${url}&model=${model}`
     })
 }
 /* 下拉菜单条件 */
@@ -202,11 +202,18 @@ const typeTagStyle = ref([
         text: '招标代理单位'
     },
 ])
+/* 创建客户 */
+function toCreateCustomer() { 
+    uni.navigateTo({
+        url: `/pages-workbench/customer-manage/create-customer/index`
+    })
+}
 </script>
 <style lang='scss' scoped>
 .customer-manage {
         background-color: #FFFFFF;
         border-radius: 16rpx;
+        margin-top: 16rpx;
         margin-bottom: 8rpx;
         z-index: 1;
 
@@ -339,8 +346,9 @@ const typeTagStyle = ref([
 
     .customer-footer {
         position: absolute;
-        bottom: 8rpx;
+        bottom: 12rpx;
         display: flex;
+        z-index: -1;
         justify-content: center;
         color: #B8B8B8;
         font-size: 24rpx;

+ 16 - 8
src/pages/tab-bar/workbench/index.vue

@@ -2,11 +2,12 @@
     <view class="workbench">
         <!-- 123456 -->
         <up-sticky>
-            <u-tabs :list="tabsList" :activeStyle="{ color: '#2B69F8' }" lineWidth="30" :current="current" @click="switchTab"></u-tabs>
+            <u-tabs :list="tabsList" :activeStyle="{ color: '#2B69F8' }" lineWidth="30" :current="current"
+                @click="switchTab"></u-tabs>
         </up-sticky>
-        <CustomerManage v-if="current === 0"/>
-        <ProjectManage v-if="current === 1"/>
-        <ServiceProviderManage v-if="current === 2"/>
+        <CustomerManage v-if="current === 0" />
+        <ProjectManage v-if="current === 1" />
+        <ServiceProviderManage v-if="current === 2" />
     </view>
 </template>
 <script setup>
@@ -33,15 +34,22 @@ const tabsList = [
     },
 ];
 function switchTab(item) {
-    const {index,name,value} =item
+    const { index, name, value } = item
     current.value = index
 }
-/* 状态栏高度 */
+/* 头部高度 */
 const statusBarHeight = ref(0)
 
 onLoad(() => {
-    statusBarHeight.value = uni.getSystemInfoSync().statusBarHeight + 'px'
-    console.log(statusBarHeight.value);
+    uni.getSystemInfo({
+        success: (res) => {
+            const statusBar = res.statusBarHeight
+            // #ifdef MP-WEIXIN
+            const custom = wx.getMenuButtonBoundingClientRect()
+            statusBarHeight.value = (custom.bottom + custom.top - statusBar) + 'px'
+            // #endif
+        }
+    })
 })
 
 </script>

BIN
src/static/images/common-search/noResult.png