|
|
@@ -0,0 +1,468 @@
|
|
|
+<script setup lang="tsx">
|
|
|
+import { Form, FormSchema } from '@/components/Form'
|
|
|
+import { UpImgButtom } from '@/components/UpFile'
|
|
|
+import { useForm } from '@/hooks/web/useForm'
|
|
|
+import { PropType, watch, ref, onMounted, unref } from 'vue'
|
|
|
+import { useValidator } from '@/hooks/web/useValidator'
|
|
|
+import { cloneDeep } from 'lodash-es'
|
|
|
+import { ElAutocomplete, ElAvatar, ElText } from 'element-plus'
|
|
|
+import { getConfigKey, getMapSearch } from '@/api/system/admin'
|
|
|
+import { mapAddressData } from '@/api/system/admin/types'
|
|
|
+import { UserList } from '@/components/UserList'
|
|
|
+import { BaseButton } from '@/components/Button'
|
|
|
+
|
|
|
+const mapKey = ref('')
|
|
|
+const actionAddress = ref<mapAddressData>()
|
|
|
+const { required } = useValidator()
|
|
|
+const center = ref({ lat: 28.655759, lng: 121.420808 })
|
|
|
+const geometries = ref([{ styleId: 'marker', position: { lat: 28.655759, lng: 121.420808 } }])
|
|
|
+const userDetail = ref<any>()
|
|
|
+const showDrawer = ref(false)
|
|
|
+
|
|
|
+const checkedUser = async (res: any) => {
|
|
|
+ userDetail.value = res
|
|
|
+ setValues({
|
|
|
+ uid: res.uid
|
|
|
+ // name: res.real_name,
|
|
|
+ // avatar: [res.avatar],
|
|
|
+ // birth_day_time: res.birthday,
|
|
|
+ // phone: res.phone
|
|
|
+ })
|
|
|
+ showDrawer.value = false
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ const res = await getConfigKey('tengxun_map_key')
|
|
|
+ if (res) {
|
|
|
+ mapKey.value = res.data.tengxun_map_key
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ currentRow: {
|
|
|
+ type: Object as PropType<any>,
|
|
|
+ default: () => null
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const selectAddress = (item: mapAddressData) => {
|
|
|
+ if (!item || !item.adcode || !item.lng || !item.lat) {
|
|
|
+ console.error('Invalid input item:', item)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const adcodeStr = String(item.adcode).padStart(6, '0') // 确保 adcode 至少有6位
|
|
|
+ const province = adcodeStr.slice(0, 2)
|
|
|
+ const city = adcodeStr.slice(2, 4)
|
|
|
+ const area = adcodeStr
|
|
|
+
|
|
|
+ setValues({
|
|
|
+ province: `${province}`,
|
|
|
+ city: `${province}${city}00000000`,
|
|
|
+ area: `${area}000000`,
|
|
|
+ longitude: item.lng,
|
|
|
+ latitude: item.lat
|
|
|
+ })
|
|
|
+
|
|
|
+ actionAddress.value = item
|
|
|
+ center.value = {
|
|
|
+ lng: item.lng,
|
|
|
+ lat: item.lat
|
|
|
+ }
|
|
|
+ geometries.value = [{ styleId: 'marker', position: { lat: item.lat, lng: item.lng } }]
|
|
|
+}
|
|
|
+const searchAddress = async (queryString: string, cb: (arg: any) => void) => {
|
|
|
+ if (queryString) {
|
|
|
+ const { data } = await getMapSearch({
|
|
|
+ key: unref(mapKey),
|
|
|
+ keyword: queryString
|
|
|
+ })
|
|
|
+ cb(
|
|
|
+ data.map((item): mapAddressData => {
|
|
|
+ return {
|
|
|
+ adcode: item.adcode,
|
|
|
+ label: item.title,
|
|
|
+ address: item.address,
|
|
|
+ lng: item.location.lng,
|
|
|
+ lat: item.location.lat,
|
|
|
+ province: item.province,
|
|
|
+ city: item.city,
|
|
|
+ district: item.district
|
|
|
+ }
|
|
|
+ })
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const formSchema = ref<FormSchema[]>([
|
|
|
+ {
|
|
|
+ field: 'uid',
|
|
|
+ label: '用户',
|
|
|
+ component: 'CheckboxGroup',
|
|
|
+ formItemProps: {
|
|
|
+ slots: {
|
|
|
+ default: () => {
|
|
|
+ if (userDetail?.value) {
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <BaseButton size="large" onClick={() => (showDrawer.value = true)}>
|
|
|
+ <div class={'flex items-center'}>
|
|
|
+ <ElAvatar shape="circle" size="small" src={userDetail?.value?.avatar}>
|
|
|
+ <img src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" />
|
|
|
+ </ElAvatar>
|
|
|
+ <div class={'flex flex-col ml-5px items-start justify-start'}>
|
|
|
+ <ElText class={'self-start!'}>{userDetail?.value?.nickname}</ElText>
|
|
|
+ <ElText size="small" class={'self-start!'}>
|
|
|
+ UID:{userDetail?.value?.uid}
|
|
|
+ </ElText>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </BaseButton>
|
|
|
+ </>
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <BaseButton onClick={() => (showDrawer.value = true)}>选择用户</BaseButton>
|
|
|
+ </>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'type',
|
|
|
+ label: '类型',
|
|
|
+ component: 'Select',
|
|
|
+ colProps: {
|
|
|
+ span: 24
|
|
|
+ },
|
|
|
+ value: 1,
|
|
|
+ componentProps: {
|
|
|
+ options: [
|
|
|
+ {
|
|
|
+ label: '自营',
|
|
|
+ value: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '加盟',
|
|
|
+ value: 2
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'name',
|
|
|
+ label: '门店名称',
|
|
|
+ component: 'Input',
|
|
|
+ colProps: {
|
|
|
+ span: 24
|
|
|
+ },
|
|
|
+ componentProps: {
|
|
|
+ placeholder: '请输入门店名称'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'logo',
|
|
|
+ label: '门店LOGO',
|
|
|
+ component: 'CheckboxGroup',
|
|
|
+ componentProps: {
|
|
|
+ placeholder: '选择LOGO'
|
|
|
+ },
|
|
|
+ formItemProps: {
|
|
|
+ slots: {
|
|
|
+ default: (data) => {
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <UpImgButtom v-model={data.logo}></UpImgButtom>
|
|
|
+ </>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'introduction',
|
|
|
+ label: '简介',
|
|
|
+ component: 'Input',
|
|
|
+ colProps: {
|
|
|
+ span: 24
|
|
|
+ },
|
|
|
+ componentProps: {
|
|
|
+ placeholder: '请输入门店简介'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'slide_images',
|
|
|
+ label: '推荐图',
|
|
|
+ component: 'CheckboxGroup',
|
|
|
+ componentProps: {
|
|
|
+ placeholder: '选择推荐图'
|
|
|
+ },
|
|
|
+ formItemProps: {
|
|
|
+ slots: {
|
|
|
+ default: (data) => {
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <UpImgButtom v-model={data.slide_images}></UpImgButtom>
|
|
|
+ </>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'manager_name',
|
|
|
+ label: '店长',
|
|
|
+ component: 'Input',
|
|
|
+ colProps: {
|
|
|
+ span: 24
|
|
|
+ },
|
|
|
+ componentProps: {
|
|
|
+ placeholder: '请输入店长名称'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'phone',
|
|
|
+ label: '联系电话',
|
|
|
+ component: 'Input',
|
|
|
+ colProps: {
|
|
|
+ span: 24
|
|
|
+ },
|
|
|
+ componentProps: {
|
|
|
+ placeholder: '请输入联系电话'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'store_account',
|
|
|
+ label: '登录账号',
|
|
|
+ component: 'Input',
|
|
|
+ colProps: {
|
|
|
+ span: 24
|
|
|
+ },
|
|
|
+ componentProps: {
|
|
|
+ strength: true,
|
|
|
+ placeholder: '请输入登录账号'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'store_password',
|
|
|
+ label: '登录密码',
|
|
|
+ colProps: {
|
|
|
+ span: 24
|
|
|
+ },
|
|
|
+ component: 'InputPassword',
|
|
|
+ componentProps: {
|
|
|
+ strength: true,
|
|
|
+ placeholder: '请输入登录密码'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // {
|
|
|
+ // field: 'true_pwd',
|
|
|
+ // label: '确认密码',
|
|
|
+ // component: 'InputPassword',
|
|
|
+ // componentProps: {
|
|
|
+ // strength: true,
|
|
|
+ // placeholder: '请输入重复输入登录密码'
|
|
|
+ // }
|
|
|
+ // },
|
|
|
+ {
|
|
|
+ field: 'valid_range',
|
|
|
+ label: '有效距离(km)',
|
|
|
+ component: 'Input',
|
|
|
+ colProps: {
|
|
|
+ span: 24
|
|
|
+ },
|
|
|
+ componentProps: {
|
|
|
+ placeholder: '请输入有效距离'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'product_status',
|
|
|
+ colProps: {
|
|
|
+ span: 24
|
|
|
+ },
|
|
|
+ label: '自主添加商品',
|
|
|
+ component: 'Switch'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'product_verify_status',
|
|
|
+ colProps: {
|
|
|
+ span: 24
|
|
|
+ },
|
|
|
+ label: '商品免审核',
|
|
|
+ component: 'Switch'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'is_show',
|
|
|
+ colProps: {
|
|
|
+ span: 24
|
|
|
+ },
|
|
|
+ label: '是否显示',
|
|
|
+ component: 'Switch'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'detail_address',
|
|
|
+ label: '省市区地址',
|
|
|
+ colProps: {
|
|
|
+ span: 24
|
|
|
+ },
|
|
|
+ component: 'Input',
|
|
|
+ formItemProps: {
|
|
|
+ slots: {
|
|
|
+ default: (data) => {
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <ElAutocomplete
|
|
|
+ v-model={data.detail_address}
|
|
|
+ value-key="label"
|
|
|
+ onSelect={(val: any) => {
|
|
|
+ selectAddress(val)
|
|
|
+ }}
|
|
|
+ fetch-suggestions={(val, cb) => {
|
|
|
+ searchAddress(val, cb)
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {{
|
|
|
+ default: ({ item }) => {
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <div class="lh-none mt-10px">{item.label}</div>
|
|
|
+ <div class="font-size-12px lh-none mt-5px pb-10px b-b-solid b-b-light b-b-1px">
|
|
|
+ {item.address}
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ </ElAutocomplete>
|
|
|
+ </>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'province',
|
|
|
+ label: '省份',
|
|
|
+ component: 'Input',
|
|
|
+ hidden: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'city',
|
|
|
+ label: '市',
|
|
|
+ hidden: true,
|
|
|
+ component: 'Input'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'area',
|
|
|
+ label: '区',
|
|
|
+ hidden: true,
|
|
|
+ component: 'Input'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'longitude',
|
|
|
+ label: '经度',
|
|
|
+ hidden: true,
|
|
|
+ component: 'Input'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'latitude',
|
|
|
+ label: '纬度',
|
|
|
+ hidden: true,
|
|
|
+ component: 'Input'
|
|
|
+ },
|
|
|
+
|
|
|
+ {
|
|
|
+ field: 'id',
|
|
|
+ label: 'id',
|
|
|
+ hidden: true,
|
|
|
+ component: 'Input'
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
+const rules = ref({
|
|
|
+ name: [required('请填写门店名称')],
|
|
|
+ introduction: [required('请填写门店简介')],
|
|
|
+ phone: [required('请填写联系号码')],
|
|
|
+ logo: [required('请选择logo')],
|
|
|
+ slide_images: [required('请选择推荐图')],
|
|
|
+ manager_name: [required('请填写店长名称')],
|
|
|
+ valid_range: [required('请填写有效距离')],
|
|
|
+ store_account: [required('请填写登录账号')],
|
|
|
+ store_password: [required('请填写登录密码')],
|
|
|
+ detail_address: [
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ validator: (_, val, callback) => {
|
|
|
+ if (!actionAddress.value || !val) {
|
|
|
+ callback(new Error('请选择地址'))
|
|
|
+ } else {
|
|
|
+ callback()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+})
|
|
|
+
|
|
|
+const { formRegister, formMethods } = useForm()
|
|
|
+const { setValues, getFormData, getElFormExpose } = formMethods
|
|
|
+
|
|
|
+const submit = async () => {
|
|
|
+ const elForm = await getElFormExpose()
|
|
|
+ const valid = await elForm?.validate().catch((err) => {
|
|
|
+ console.log(err)
|
|
|
+ })
|
|
|
+ if (valid) {
|
|
|
+ const formData = await getFormData()
|
|
|
+ return formData
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => props.currentRow,
|
|
|
+ async (value) => {
|
|
|
+ if (!value) return
|
|
|
+ const currentRow = cloneDeep(value)
|
|
|
+ setValues(currentRow)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ deep: true,
|
|
|
+ immediate: true
|
|
|
+ }
|
|
|
+)
|
|
|
+
|
|
|
+defineExpose({
|
|
|
+ submit
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <Form :rules="rules" @register="formRegister" :schema="formSchema" />
|
|
|
+ <TlbsMap
|
|
|
+ v-if="mapKey"
|
|
|
+ ref="map"
|
|
|
+ :api-key="mapKey"
|
|
|
+ :zoom="15"
|
|
|
+ :center="center"
|
|
|
+ :control="{
|
|
|
+ scale: {},
|
|
|
+ zoom: {
|
|
|
+ position: 'topRight'
|
|
|
+ }
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <TlbsMultiMarker
|
|
|
+ :geometries="geometries"
|
|
|
+ :styles="{
|
|
|
+ marker: {
|
|
|
+ width: 20,
|
|
|
+ height: 30,
|
|
|
+ anchor: { x: 10, y: 30 }
|
|
|
+ }
|
|
|
+ }"
|
|
|
+ />
|
|
|
+ </TlbsMap>
|
|
|
+ <UserList v-model="showDrawer" @confirm="checkedUser" />
|
|
|
+</template>
|