修改目录结构;调整组件路径;访客邀约页面;
This commit is contained in:
1 parent
3d6ea75b46
commit
a813da33ee
16 files changed
+504
-532
No files matched your search
+14
-6
@@ -85,11 +85,19 @@ const tabbar: TabbarItem[] = [
|
||||
auth: ["merchant"],
|
||||
},
|
||||
{
|
||||
pagePath: "pages/tabbar/visitor",
|
||||
text: "访客",
|
||||
iconPath: "/static/tabbar/visitor.png",
|
||||
selectedIconPath: "/static/tabbar/visitor-select.png",
|
||||
launchParam: [],
|
||||
pagePath: "pages/tabbar/invitation",
|
||||
text: "访客邀约",
|
||||
iconPath: "/static/tabbar/invitation.png",
|
||||
selectedIconPath: "/static/tabbar/invitation-select.png",
|
||||
launchParam: ["invitation"],
|
||||
auth: [],
|
||||
},
|
||||
{
|
||||
pagePath: "pages/tabbar/invitationRecord",
|
||||
text: "邀约记录",
|
||||
iconPath: "/static/tabbar/invitation.png",
|
||||
selectedIconPath: "/static/tabbar/invitation-select.png",
|
||||
launchParam: ["invitation"],
|
||||
auth: [],
|
||||
},
|
||||
{
|
||||
@@ -113,7 +121,7 @@ const tabbar: TabbarItem[] = [
|
||||
text: "我的",
|
||||
iconPath: "/static/tabbar/my.png",
|
||||
selectedIconPath: "/static/tabbar/my-select.png",
|
||||
launchParam: ["canteen", "selfMerchant", "onlineShop", "coopMerchant"],
|
||||
launchParam: ["canteen", "selfMerchant", "onlineShop", "coopMerchant", "invitation"],
|
||||
auth: ["user", "merchant", "doorAdmin"],
|
||||
},
|
||||
]
|
||||
|
||||
+18
-23
@@ -73,12 +73,19 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tabbar/visitor",
|
||||
"path": "pages/tabbar/invitation",
|
||||
"style": {
|
||||
"navigationBarTitleText": "访客",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tabbar/invitationRecord",
|
||||
"style": {
|
||||
"navigationBarTitleText": "访客记录",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tabbar/message",
|
||||
"style": {
|
||||
@@ -408,30 +415,12 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/visitor",
|
||||
"root": "pages/invitation",
|
||||
"pages": [
|
||||
{
|
||||
"path": "apply",
|
||||
"path": "detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "访客申请"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "applyRecord",
|
||||
"style": {
|
||||
"navigationBarTitleText": "申请记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invitation",
|
||||
"style": {
|
||||
"navigationBarTitleText": "访客邀约"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invitationRecord",
|
||||
"style": {
|
||||
"navigationBarTitleText": "邀约记录"
|
||||
"navigationBarTitleText": "访客详情"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -607,7 +596,13 @@
|
||||
"selectedIconPath": ""
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/tabbar/visitor",
|
||||
"pagePath": "pages/tabbar/invitation",
|
||||
"text": "",
|
||||
"iconPath": "",
|
||||
"selectedIconPath": ""
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/tabbar/invitationRecord",
|
||||
"text": "",
|
||||
"iconPath": "",
|
||||
"selectedIconPath": ""
|
||||
|
||||
File renamed without changes.
@@ -0,0 +1,3 @@
|
||||
<template></template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
File renamed without changes.
File renamed without changes.
@@ -40,10 +40,10 @@ const moduleList = [
|
||||
launchParam: "flowStatistics",
|
||||
},
|
||||
{
|
||||
pagePath: "/pages/tabbar/visitor",
|
||||
pagePath: "/pages/tabbar/invitation",
|
||||
text: "访客",
|
||||
iconPath: "/static/tabbar/visitor-select.png",
|
||||
launchParam: "visitor",
|
||||
iconPath: "/static/tabbar/invitation-select.png",
|
||||
launchParam: "invitation",
|
||||
},
|
||||
{
|
||||
pagePath: "/pages/tabbar/message",
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<view class="h-20rpx" />
|
||||
<tm-sheet margin="[0]">
|
||||
<view class="mx-auto mb-1 text-center text-lg">访客邀约</view>
|
||||
<tm-alert :border="['2']" :borderColor="['#89c0ff']" :darkBorderColor="['#3b5066']">访客邀约请填写以下信息</tm-alert>
|
||||
<tm-form @submit="onsubmit" v-model="form" :rules="rules" direction="vertical">
|
||||
<tm-form-item name="name" label="访客姓名">
|
||||
<tm-input color="transparent" v-model.trim="form.name" placeholder="请输入真实姓名" />
|
||||
</tm-form-item>
|
||||
<tm-form-item name="mobile" label="访客手机号">
|
||||
<tm-input color="transparent" v-model.trim="form.mobile" placeholder="请输入访客手机号" />
|
||||
</tm-form-item>
|
||||
<tm-form-item name="companyName" label="来访公司名称">
|
||||
<tm-input color="transparent" v-model.trim="form.companyName" placeholder="来访公司名称" />
|
||||
</tm-form-item>
|
||||
<tm-form-item name="vStartDate" label="拜访开始时间">
|
||||
<tm-picker-date
|
||||
v-model="form.vStartDate"
|
||||
v-model:modelStr="_timestr"
|
||||
type="minute"
|
||||
start="2020-01-01 00:00"
|
||||
end="2080-01-01 23:59"
|
||||
>
|
||||
<tm-input
|
||||
:modelValue="form.vStartDate"
|
||||
placeholder="请选择拜访开始时间"
|
||||
style="pointer-events: none"
|
||||
disabled
|
||||
color="transparent"
|
||||
>
|
||||
<template v-slot:rightIcon>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</tm-picker-date>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="vEndDate" label="拜访离开时间">
|
||||
<tm-picker-date v-model="form.vEndDate" v-model:modelStr="_timestr" type="minute" start="2020-01-01 00:00" end="2080-01-01 23:59">
|
||||
<tm-input
|
||||
:modelValue="form.vEndDate"
|
||||
placeholder="请选择拜访离开时间"
|
||||
style="pointer-events: none"
|
||||
disabled
|
||||
color="transparent"
|
||||
>
|
||||
<template v-slot:rightIcon>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</tm-picker-date>
|
||||
</tm-form-item>
|
||||
|
||||
<tm-form-item name="deptId" label="拜访部门">
|
||||
<tm-drawer :show-close="true" :show-footer="true" size="auto" title="请选择拜访部门">
|
||||
<template #trigger>
|
||||
<tm-input :modelValue="form.deptId" placeholder="请选择拜访部门" style="pointer-events: none" disabled color="transparent">
|
||||
<template v-slot:rightIcon>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</template>
|
||||
<tm-text color="#999999">精致美观的对话框,可全局配置风格</tm-text>
|
||||
</tm-drawer>
|
||||
</tm-form-item>
|
||||
|
||||
<tm-form-item name="reasonValue" label="拜访原因">
|
||||
<tm-picker v-model="form.reasonValue" v-model:modelStr="_pingtaistr" rangText="reasonValue" :list="visitorReasonList">
|
||||
<tm-input :modelValue="_pingtaistr" placeholder="请选择拜访原因" style="pointer-events: none" disabled color="transparent">
|
||||
<template #rightIcon>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</tm-picker>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="isOtherPeople" label="是否可随行人员" direction="horizontal" labelWidth="500">
|
||||
<tm-switch v-model="form.isOtherPeople" />
|
||||
</tm-form-item>
|
||||
<tm-form-item name="isDrive" label="是否可驾驶车辆" direction="horizontal" labelWidth="500">
|
||||
<tm-switch v-model="form.isDrive" />
|
||||
</tm-form-item>
|
||||
<tm-button form-type="submit" block>提交</tm-button>
|
||||
</tm-form>
|
||||
</tm-sheet>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
import { reactive, ref } from "vue"
|
||||
import { invitationApi } from "@/sheep/api/index"
|
||||
|
||||
const form = reactive({
|
||||
name: "",
|
||||
mobile: "",
|
||||
companyName: "",
|
||||
vStartDate: "",
|
||||
vEndDate: "",
|
||||
deptId: "",
|
||||
reasonValue: "",
|
||||
isOtherPeople: true,
|
||||
isDrive: true,
|
||||
})
|
||||
const _timestr = ref("2025-01-01")
|
||||
const _pingtaistr = ref("")
|
||||
const visitorReasonList = ref([])
|
||||
|
||||
onLoad(() => {
|
||||
const app = getApp()
|
||||
app._initTabbar()
|
||||
|
||||
getVisitorReasonList()
|
||||
})
|
||||
|
||||
// 获取拜访原因列表
|
||||
const getVisitorReasonList = async () => {
|
||||
const { code, data } = await invitationApi.getDictListByType("1")
|
||||
if (code == 0 && data) {
|
||||
visitorReasonList.value = data
|
||||
return
|
||||
}
|
||||
visitorReasonList.value = []
|
||||
}
|
||||
|
||||
const rules: Record<string, TM.FORM_RULE[]> = {
|
||||
name: [{ required: true, message: "姓名2到5个字符", rule: /^.{2,5}$/ }],
|
||||
mobile: [{ required: true, message: "请输入正确的手机号", rule: /^(?:(?:\+|00)86)?1\d{10}$/ }],
|
||||
companyName: [{ required: true, message: "请输入来访公司名称" }],
|
||||
vStartDate: [{ required: true, message: "请选择拜访开始时间" }],
|
||||
vEndDate: [{ required: true, message: "请选择拜访离开时间" }],
|
||||
// deptId: [{ required: true, message: "请选择拜访部门" }],
|
||||
// reasonValue: [{ required: true, message: "请选择拜访原因" }],
|
||||
}
|
||||
|
||||
// 表单提交
|
||||
const onsubmit = (evt: TM.FORM_SUBMIT_RESULT) => {
|
||||
console.log("校验结果:", evt)
|
||||
if (!evt.isPass) return
|
||||
uni.showModal({ title: "提示", content: "确认提交吗?", confirmColor: "#0094ff" })
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template></template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
@@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<view class="h-30rpx" />
|
||||
<tm-sheet :margin="[0]">
|
||||
<view class="flex-row items-center justify-center flex-wrap">
|
||||
<view class="w-half flex justify-center py-2 rounded-1">
|
||||
<view class="w-half text-center rounded-1 active:bg-#e8e8e8 duration-200" @click="router.jump('/pages/visitor/apply')">
|
||||
<image class="w-80 h-80 px-2 pt-2" src="@/static/page/货柜详情.svg" />
|
||||
<tm-text font-size="32">访客申请</tm-text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="w-half flex justify-center py-2 rounded-1">
|
||||
<view class="w-half text-center rounded-1 active:bg-#e8e8e8 duration-200" @click="router.jump('/pages/visitor/applyRecord')">
|
||||
<image class="w-80 h-80 px-2 pt-2" src="@/static/page/货柜详情.svg" />
|
||||
<tm-text font-size="32">申请记录</tm-text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="w-half flex justify-center py-2 rounded-1">
|
||||
<view class="w-half text-center rounded-1 active:bg-#e8e8e8 duration-200" @click="router.jump('/pages/visitor/invitation')">
|
||||
<image class="w-80 h-80 px-2 pt-2" src="@/static/page/货柜详情.svg" />
|
||||
<tm-text font-size="32">访客邀约</tm-text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="w-half flex justify-center py-2 rounded-1">
|
||||
<view class="w-half text-center rounded-1 active:bg-#e8e8e8 duration-200" @click="router.jump('/pages/visitor/invitationRecord')">
|
||||
<image class="w-80 h-80 px-2 pt-2" src="@/static/page/货柜详情.svg" />
|
||||
<tm-text font-size="32">邀约记录</tm-text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="w-half flex justify-center py-2 rounded-1">
|
||||
<view class="w-half text-center rounded-1 active:bg-#e8e8e8 duration-200">
|
||||
<image class="w-80 h-80 px-2 pt-2" src="@/static/page/货柜详情.svg" />
|
||||
<tm-text font-size="32">核销</tm-text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</tm-sheet>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
import { router } from "@/sheep"
|
||||
|
||||
onLoad(() => {
|
||||
const app = getApp()
|
||||
app._initTabbar()
|
||||
})
|
||||
</script>
|
||||
@@ -1,174 +0,0 @@
|
||||
<template>
|
||||
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<view class="h-30rpx" />
|
||||
|
||||
<tm-sheet margin="[0, 20]">
|
||||
<view class="mx-auto text-center text-lg">访客申请</view>
|
||||
<tm-text color="#999999" class="bg-#FFF7E6 p-1 pl-2 my-1">!访客需要填写以下验证信息</tm-text>
|
||||
<tm-form @submit="onsubmit" v-model="form" :rules="rules">
|
||||
<tm-form-item name="username" label="您的姓名">
|
||||
<tm-input color="transparent" v-model="form.username" placeholder="姓名" />
|
||||
</tm-form-item>
|
||||
<tm-form-item name="username" label="联系方式">
|
||||
<tm-input color="transparent" v-model="form.username" placeholder="联系方式" />
|
||||
</tm-form-item>
|
||||
<tm-form-item name="username" label="公司名称">
|
||||
<tm-input color="transparent" v-model="form.username" placeholder="公司名称" />
|
||||
</tm-form-item>
|
||||
<tm-form-item name="pingtai" label="拜访原因">
|
||||
<tm-picker
|
||||
v-model="form.pingtai"
|
||||
v-model:modelStr="_pingtaistr"
|
||||
rangText="name"
|
||||
:list="[
|
||||
{ id: 1, name: '1111' },
|
||||
{ id: 2, name: '2222' },
|
||||
{ id: 3, name: '3333' },
|
||||
]"
|
||||
>
|
||||
<tm-input :modelValue="_pingtaistr" placeholder="请选择拜访原因" style="pointer-events: none" disabled color="transparent">
|
||||
<template v-slot:rightIcon>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</tm-picker>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="username" label="随行人员">
|
||||
<view class="flex items-center" v-for="(v, i) in form.entourage" :key="i">
|
||||
<tm-input color="transparent" v-model="form.entourage[i].username" placeholder="姓名" />
|
||||
|
||||
<tm-input color="transparent" v-model="form.entourage[i].phone" placeholder="手机号" />
|
||||
|
||||
<view class="w-80rpx">
|
||||
<tm-button v-if="i > 0 && i < 6" color="error" size="s" btnIcon width="40" rounded @click="removeEntourage(i)">—</tm-button>
|
||||
</view>
|
||||
</view>
|
||||
<tm-button skin="thin" block size="s" class="my-2" @click="addEntourage">添加</tm-button>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="username" label="车牌号">
|
||||
<view v-for="(v, i) in form.carPlate" :key="i" class="flex items-center">
|
||||
<tm-input color="transparent" v-model="form.carPlate[i]" :placeholder="`车牌号${i + 1}`" />
|
||||
|
||||
<view class="w-80rpx">
|
||||
<tm-button v-if="i > 0 && i < 6" color="error" size="s" btnIcon width="40" rounded @click="removeCarPlate(i)">—</tm-button>
|
||||
</view>
|
||||
</view>
|
||||
<tm-button skin="thin" block size="s" class="my-2" @click="addCarPlate">添加</tm-button>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="pingtai" label="到访单位">
|
||||
<tm-picker
|
||||
v-model="form.pingtai"
|
||||
v-model:modelStr="_pingtaistr"
|
||||
rangText="name"
|
||||
:list="[
|
||||
{ id: 1, name: '1111' },
|
||||
{ id: 2, name: '2222' },
|
||||
{ id: 3, name: '3333' },
|
||||
]"
|
||||
>
|
||||
<tm-input :modelValue="_pingtaistr" placeholder="请选择到访单位" style="pointer-events: none" disabled color="transparent">
|
||||
<template v-slot:rightIcon>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</tm-picker>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="username" label="接待人">
|
||||
<tm-input color="transparent" v-model="form.username" placeholder="接待人" />
|
||||
</tm-form-item>
|
||||
<tm-form-item name="time" label="到访时间">
|
||||
<tm-picker-date v-model="form.time" v-model:modelStr="_timestr" type="minute" start="2020-01-01 00:00" end="2080-01-01 23:59">
|
||||
<tm-input :modelValue="form.time" placeholder="请选择到访时间" style="pointer-events: none" disabled color="transparent">
|
||||
<template v-slot:rightIcon>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</tm-picker-date>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="textarea" label="备注">
|
||||
<tm-input v-model="form.textarea" :maxlength="100" placeholder="请输入" type="textarea" show-char-count color="transparent">
|
||||
</tm-input>
|
||||
</tm-form-item>
|
||||
|
||||
<hr class="mb-4" />
|
||||
<tm-button form-type="submit" block>提交</tm-button>
|
||||
</tm-form>
|
||||
</tm-sheet>
|
||||
|
||||
<view class="h-30rpx" />
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from "vue"
|
||||
|
||||
const form = reactive({
|
||||
username: "",
|
||||
price: "",
|
||||
checkbox: [],
|
||||
radio: "",
|
||||
time: "2024-6-5",
|
||||
pingtai: [],
|
||||
store: 0,
|
||||
score: 0,
|
||||
textarea: "",
|
||||
entourage: [
|
||||
{
|
||||
username: "",
|
||||
phone: "",
|
||||
},
|
||||
],
|
||||
carPlate: [""],
|
||||
})
|
||||
|
||||
const _timestr = ref(form.time)
|
||||
const _pingtaistr = ref("")
|
||||
|
||||
const rules: Record<string, TM.FORM_RULE[]> = {
|
||||
username: [{ required: true, message: "姓名2到5个字符", rule: /^.{2,5}$/ }],
|
||||
address: [
|
||||
{ required: true, message: "地址不能为空" },
|
||||
{ required: true, message: "长度要小于3", max: 2 },
|
||||
],
|
||||
price: [{ required: true, message: "价格要大于等于99", type: "number", min: 99 }],
|
||||
checkbox: [{ required: true }],
|
||||
radio: [{ required: true }],
|
||||
time: [{ required: true, type: "date" }],
|
||||
pingtai: [{ required: true }],
|
||||
store: [{ required: true, min: 1, max: 100, message: "介于1-100之间" }],
|
||||
score: [{ required: true, min: 5, max: 5, message: "必须打5分哦" }],
|
||||
textarea: [{ required: true, max: 100, message: "请输入内容" }],
|
||||
}
|
||||
|
||||
// 添加随从
|
||||
const addEntourage = () => {
|
||||
if (form.entourage.length > 5) return uni.showToast({ title: "最多添加6个随从", icon: "none" })
|
||||
form.entourage.push({
|
||||
username: "",
|
||||
phone: "",
|
||||
})
|
||||
}
|
||||
|
||||
// 移除随从
|
||||
const removeEntourage = (index: number) => {
|
||||
form.entourage.splice(index, 1)
|
||||
}
|
||||
|
||||
// 添加车牌号
|
||||
const addCarPlate = () => {
|
||||
if (form.carPlate.length > 3) return uni.showToast({ title: "最多添加4个车牌", icon: "none" })
|
||||
form.carPlate.push("")
|
||||
}
|
||||
|
||||
// 移除车牌号
|
||||
const removeCarPlate = (index: number) => {
|
||||
form.carPlate.splice(index, 1)
|
||||
}
|
||||
|
||||
// 表单提交
|
||||
const onsubmit = (evt: TM.FORM_SUBMIT_RESULT) => {
|
||||
console.log("校验结果:", evt)
|
||||
if (!evt.isPass) return
|
||||
uni.showModal({ title: "提示", content: "确认提交吗?", confirmColor: "#0094ff" })
|
||||
}
|
||||
</script>
|
||||
@@ -35,3 +35,5 @@ export { default as individualOrderApi } from "./individualOrder"
|
||||
export { default as hardApi } from "./hard"
|
||||
|
||||
export { default as noticeApi } from "./notice"
|
||||
|
||||
export { default as invitationApi } from "./invitation"
|
||||
@@ -0,0 +1,17 @@
|
||||
import request from "@/sheep/utils/request"
|
||||
|
||||
export default {
|
||||
// 获取拜访原因字典
|
||||
getDictListByType: (type: number | string): Promise<any> =>
|
||||
request({
|
||||
url: `/integrated/app/visitor/getDictListByType?type=${type}`,
|
||||
method: "GET",
|
||||
}),
|
||||
}
|
||||
|
||||
// const updateCheckStatus = () => {
|
||||
// request({
|
||||
// url: `/integrated/visitors/app/updateCheckStatus?id=1950037876011745282&checkStatus=2`,
|
||||
// method: "GET",
|
||||
// })
|
||||
// }
|
||||
File renamed without changes.
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
File renamed without changes.
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
@@ -1,8 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, onMounted, watch, PropType } from "vue";
|
||||
import { arrayNumberValid, getUid, arrayNumberValidByStyleMP, arrayNumberValidByStyleBorderColor, arrayNumberValidByStyleBorderStyle, covetUniNumber, linearValid, getUnit } from "../../libs/tool";
|
||||
import { useTmConfig } from "../../libs/config";
|
||||
import { getDefaultColor, getDefaultColorObj, getOutlineColorObj, getTextColorObj, getThinColorObj } from "../../libs/colors";
|
||||
import { computed, ref, onMounted, watch } from "vue"
|
||||
import {
|
||||
arrayNumberValid,
|
||||
getUid,
|
||||
arrayNumberValidByStyleMP,
|
||||
arrayNumberValidByStyleBorderColor,
|
||||
arrayNumberValidByStyleBorderStyle,
|
||||
covetUniNumber,
|
||||
linearValid,
|
||||
getUnit,
|
||||
} from "../../libs/tool"
|
||||
import { useTmConfig } from "../../libs/config"
|
||||
import { getDefaultColor, getDefaultColorObj, getOutlineColorObj, getTextColorObj, getThinColorObj } from "../../libs/colors"
|
||||
/**
|
||||
* @displayName 开关
|
||||
* @exportName tm-switch
|
||||
@@ -13,359 +22,376 @@ import { getDefaultColor, getDefaultColorObj, getOutlineColorObj, getTextColorOb
|
||||
| --- | --- | --- | --- |
|
||||
| ☑️| ☑️ | ☑️ | ☑️ | ☑️ | 1.0.0 |
|
||||
*/
|
||||
defineOptions({ name: 'TmSwitch' });
|
||||
defineOptions({ name: "TmSwitch" })
|
||||
const { config } = useTmConfig()
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 激活时的背景色,空值时取全局的值。
|
||||
*/
|
||||
color: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
/**
|
||||
* 未激活时的背景
|
||||
*/
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: "info"
|
||||
},
|
||||
/**
|
||||
* 未激活时的暗黑背景
|
||||
* 空取inputDarkColor
|
||||
*/
|
||||
darkBgColor: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
/**
|
||||
* 按钮的背景色
|
||||
*/
|
||||
btnColor: {
|
||||
type: String,
|
||||
default: "white"
|
||||
},
|
||||
/**
|
||||
* 尺寸
|
||||
*/
|
||||
size: {
|
||||
type: String as PropType<"small" | "normal" | "large">,
|
||||
default: "normal"
|
||||
},
|
||||
/**
|
||||
* 间隙,px单位
|
||||
*/
|
||||
space: {
|
||||
type: [Number, String],
|
||||
default: "2px"
|
||||
},
|
||||
/**
|
||||
* 当前打开的状态,默认为false
|
||||
* 等同v-model=""
|
||||
*/
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* 是否加载中
|
||||
*/
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* 开关文字数组第一个为开,后一个为关
|
||||
*/
|
||||
label: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: (): string[] => [],
|
||||
validator(val: string[]): boolean {
|
||||
if (!Array.isArray(val)) return false;
|
||||
if (val.length == 0) return true;
|
||||
if (val.length != 2) {
|
||||
console.error("x:必须长度为2")
|
||||
return false;
|
||||
}
|
||||
return true
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 圆角。空值时取全局值。
|
||||
*/
|
||||
round: {
|
||||
type: [String, Number],
|
||||
default: ""
|
||||
}
|
||||
});
|
||||
/**
|
||||
* 激活时的背景色,空值时取全局的值。
|
||||
*/
|
||||
color: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
/**
|
||||
* 未激活时的背景
|
||||
*/
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: "info",
|
||||
},
|
||||
/**
|
||||
* 未激活时的暗黑背景
|
||||
* 空取inputDarkColor
|
||||
*/
|
||||
darkBgColor: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
/**
|
||||
* 按钮的背景色
|
||||
*/
|
||||
btnColor: {
|
||||
type: String,
|
||||
default: "white",
|
||||
},
|
||||
/**
|
||||
* 尺寸
|
||||
*/
|
||||
size: {
|
||||
type: String,
|
||||
default: "normal",
|
||||
},
|
||||
/**
|
||||
* 间隙,px单位
|
||||
*/
|
||||
space: {
|
||||
type: [Number, String],
|
||||
default: "2px",
|
||||
},
|
||||
/**
|
||||
* 当前打开的状态,默认为false
|
||||
* 等同v-model=""
|
||||
*/
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* 是否加载中
|
||||
*/
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* 开关文字数组第一个为开,后一个为关
|
||||
*/
|
||||
label: {
|
||||
type: Array,
|
||||
default: (): string[] => [],
|
||||
validator(val: string[]): boolean {
|
||||
if (!Array.isArray(val)) return false
|
||||
if (val.length == 0) return true
|
||||
if (val.length != 2) {
|
||||
console.error("x:必须长度为2")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
/**
|
||||
* 圆角。空值时取全局值。
|
||||
*/
|
||||
round: {
|
||||
type: [String, Number],
|
||||
default: "",
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
/**
|
||||
* 状态变换时触发。
|
||||
* @param status - 当前的开关状态
|
||||
*/
|
||||
(e: 'change', status: boolean): void;
|
||||
/**
|
||||
* 组件被点击时触发。
|
||||
* @param status - 当前的开关状态,这里的状态是在变更前。
|
||||
*/
|
||||
(e: 'click', status: boolean): void;
|
||||
(e: 'update:modelValue', value: boolean): void;
|
||||
}>();
|
||||
/**
|
||||
* 状态变换时触发。
|
||||
* @param status - 当前的开关状态
|
||||
*/
|
||||
(e: "change", status: boolean): void
|
||||
/**
|
||||
* 组件被点击时触发。
|
||||
* @param status - 当前的开关状态,这里的状态是在变更前。
|
||||
*/
|
||||
(e: "click", status: boolean): void
|
||||
(e: "update:modelValue", value: boolean): void
|
||||
}>()
|
||||
|
||||
const id = ref(`tmSwitch${getUid()}`);
|
||||
const boxwidth = ref(0);
|
||||
const boxheight = ref(0);
|
||||
const opened = ref(false);
|
||||
const id = ref(`tmSwitch${getUid()}`)
|
||||
const boxwidth = ref(0)
|
||||
const boxheight = ref(0)
|
||||
const opened = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
opened.value = props.modelValue;
|
||||
getNodes();
|
||||
});
|
||||
opened.value = props.modelValue
|
||||
getNodes()
|
||||
})
|
||||
|
||||
const _round = computed((): string => {
|
||||
if (props.round == "") {
|
||||
return covetUniNumber(config.switchRadius, config.unit)
|
||||
}
|
||||
return covetUniNumber(props.round, config.unit)
|
||||
});
|
||||
if (props.round == "") {
|
||||
return covetUniNumber(config.switchRadius, config.unit)
|
||||
}
|
||||
return covetUniNumber(props.round, config.unit)
|
||||
})
|
||||
|
||||
const _bgColor = computed((): string => {
|
||||
if (config.mode == 'dark') {
|
||||
if (props.darkBgColor != '') return props.darkBgColor
|
||||
return config.inputDarkColor;
|
||||
}
|
||||
return getDefaultColor(props.bgColor)
|
||||
});
|
||||
if (config.mode == "dark") {
|
||||
if (props.darkBgColor != "") return props.darkBgColor
|
||||
return config.inputDarkColor
|
||||
}
|
||||
return getDefaultColor(props.bgColor)
|
||||
})
|
||||
|
||||
const _activeBgColor = computed((): string => {
|
||||
if (props.color == "") {
|
||||
return getDefaultColor(config.color)
|
||||
}
|
||||
return getDefaultColor(props.color)
|
||||
});
|
||||
if (props.color == "") {
|
||||
return getDefaultColor(config.color)
|
||||
}
|
||||
return getDefaultColor(props.color)
|
||||
})
|
||||
|
||||
const _btnColor = computed((): string => {
|
||||
return getDefaultColor(props.btnColor)
|
||||
});
|
||||
return getDefaultColor(props.btnColor)
|
||||
})
|
||||
|
||||
const _size = computed((): string => {
|
||||
if (props.size == "small") return "44rpx"
|
||||
if (props.size == "large") return "76rpx"
|
||||
return "60rpx"
|
||||
});
|
||||
if (props.size == "small") return "44rpx"
|
||||
if (props.size == "large") return "76rpx"
|
||||
return "60rpx"
|
||||
})
|
||||
|
||||
const _fontSize = computed((): string => {
|
||||
if (props.size == "small") return "20rpx"
|
||||
if (props.size == "large") return "24rpx"
|
||||
return "22rpx"
|
||||
});
|
||||
if (props.size == "small") return "20rpx"
|
||||
if (props.size == "large") return "24rpx"
|
||||
return "22rpx"
|
||||
})
|
||||
|
||||
const _sizeWidth = computed((): string => {
|
||||
if (props.size == "small") return "88rpx"
|
||||
if (props.size == "large") return "152rpx"
|
||||
return "120rpx"
|
||||
});
|
||||
if (props.size == "small") return "88rpx"
|
||||
if (props.size == "large") return "152rpx"
|
||||
return "120rpx"
|
||||
})
|
||||
|
||||
const _space = computed((): number => {
|
||||
let space = covetUniNumber(props.space)
|
||||
let spaceUnit = getUnit(space)
|
||||
let spacenumber = parseInt(space)
|
||||
if (spaceUnit == 'rpx') {
|
||||
spacenumber = uni.upx2px(parseInt(space))
|
||||
}
|
||||
let space = covetUniNumber(props.space)
|
||||
let spaceUnit = getUnit(space)
|
||||
let spacenumber = parseInt(space)
|
||||
if (spaceUnit == "rpx") {
|
||||
spacenumber = uni.upx2px(parseInt(space))
|
||||
}
|
||||
|
||||
return spacenumber
|
||||
});
|
||||
return spacenumber
|
||||
})
|
||||
|
||||
const _contentWidth = computed((): number => {
|
||||
return boxwidth.value - _space.value * 2
|
||||
});
|
||||
return boxwidth.value - _space.value * 2
|
||||
})
|
||||
|
||||
const _contentHeight = computed((): number => {
|
||||
return boxheight.value - _space.value * 2
|
||||
});
|
||||
return boxheight.value - _space.value * 2
|
||||
})
|
||||
|
||||
const _maxLeftPos = computed((): number => {
|
||||
return _contentWidth.value - _contentHeight.value
|
||||
});
|
||||
return _contentWidth.value - _contentHeight.value
|
||||
})
|
||||
|
||||
const _label = computed((): string[] => {
|
||||
return props.label;
|
||||
});
|
||||
return props.label
|
||||
})
|
||||
|
||||
const _loading = computed((): boolean => {
|
||||
return props.loading;
|
||||
});
|
||||
return props.loading
|
||||
})
|
||||
|
||||
const _disabled = computed((): boolean => {
|
||||
return props.disabled;
|
||||
});
|
||||
return props.disabled
|
||||
})
|
||||
|
||||
const _animationFun = computed((): string => {
|
||||
return config.animation
|
||||
});
|
||||
return config.animation
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, (newvalue: boolean) => {
|
||||
if (newvalue != opened.value) {
|
||||
opened.value = newvalue;
|
||||
}
|
||||
});
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newvalue: boolean) => {
|
||||
if (newvalue != opened.value) {
|
||||
opened.value = newvalue
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
function onClick() {
|
||||
/**
|
||||
* 组件被点击时触发。
|
||||
* @param status {boolean} 当前的开关状态,这里的状态是在变更前。
|
||||
*/
|
||||
emit("click", opened.value)
|
||||
if (_loading.value || _disabled.value) return;
|
||||
opened.value = !opened.value
|
||||
/**
|
||||
* 状态变换时触发。
|
||||
* @param status {boolean} 当前的开关状态
|
||||
*/
|
||||
emit("change", opened.value)
|
||||
/**
|
||||
* 等同v-model=""
|
||||
*/
|
||||
emit("update:modelValue", opened.value)
|
||||
/**
|
||||
* 组件被点击时触发。
|
||||
* @param status {boolean} 当前的开关状态,这里的状态是在变更前。
|
||||
*/
|
||||
emit("click", opened.value)
|
||||
if (_loading.value || _disabled.value) return
|
||||
opened.value = !opened.value
|
||||
/**
|
||||
* 状态变换时触发。
|
||||
* @param status {boolean} 当前的开关状态
|
||||
*/
|
||||
emit("change", opened.value)
|
||||
/**
|
||||
* 等同v-model=""
|
||||
*/
|
||||
emit("update:modelValue", opened.value)
|
||||
}
|
||||
|
||||
function getNodes() {
|
||||
let width = covetUniNumber(_sizeWidth.value)
|
||||
let height = covetUniNumber(_size.value)
|
||||
let widthUnit = getUnit(width)
|
||||
let heightUnit = getUnit(height)
|
||||
if (widthUnit == 'rpx') {
|
||||
width = uni.upx2px(parseInt(width)) + ''
|
||||
}
|
||||
if (heightUnit == 'rpx') {
|
||||
height = uni.upx2px(parseInt(height)) + ''
|
||||
}
|
||||
boxwidth.value = parseInt(width);
|
||||
boxheight.value = parseInt(height);
|
||||
|
||||
let width = covetUniNumber(_sizeWidth.value)
|
||||
let height = covetUniNumber(_size.value)
|
||||
let widthUnit = getUnit(width)
|
||||
let heightUnit = getUnit(height)
|
||||
if (widthUnit == "rpx") {
|
||||
width = uni.upx2px(parseInt(width)) + ""
|
||||
}
|
||||
if (heightUnit == "rpx") {
|
||||
height = uni.upx2px(parseInt(height)) + ""
|
||||
}
|
||||
boxwidth.value = parseInt(width)
|
||||
boxheight.value = parseInt(height)
|
||||
}
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
options: {
|
||||
styleIsolation: "apply-shared",
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
multipleSlots: true,
|
||||
},
|
||||
};
|
||||
options: {
|
||||
styleIsolation: "apply-shared",
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
multipleSlots: true,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<view @click="onClick" :id="id" class="tmSwitch" ref="tmSwitch" :style="{
|
||||
height: boxheight+'px',
|
||||
width: boxwidth+'px',
|
||||
backgroundColor: _bgColor,
|
||||
borderRadius: _round
|
||||
}">
|
||||
<view :class="[_disabled ? 'tmSwitchDisabled' : '', opened ? 'tmSwitchBgOn' : 'tmSwitchOff']" class="tmSwitchBg"
|
||||
:style="{ backgroundColor: _activeBgColor, borderRadius: _round, 'transition-timing-function': _animationFun }">
|
||||
</view>
|
||||
<view :class="[_disabled ? 'tmSwitchDisabled' : '']" class="tmSwitchWrap" :style="{
|
||||
left: _space + 'px',
|
||||
top: _space + 'px',
|
||||
width: _contentWidth + 'px',
|
||||
height: _contentHeight + 'px',
|
||||
borderRadius: _round
|
||||
}">
|
||||
<view v-if="_label.length == 2" class="tmSwitchText">
|
||||
<text class="tmSwitchTextLeft" :style="{ fontSize: _fontSize }">{{ !opened ? '' : _label[0] }}</text>
|
||||
<text class="tmSwitchTextRight" :style="{ fontSize: _fontSize }">{{ opened ? '' : _label[1] }}</text>
|
||||
</view>
|
||||
<view class="tmSwitchBtn" :style="{
|
||||
width: _contentHeight + 'px',
|
||||
height: _contentHeight + 'px',
|
||||
transform: `translateX(${opened ? _maxLeftPos : 0}px)`,
|
||||
backgroundColor: _btnColor,
|
||||
borderRadius: _round,
|
||||
'transition-timing-function': _animationFun
|
||||
}">
|
||||
<tm-icon v-if="_loading" :spin="true" size="24" name="loader-line" :color="_activeBgColor"></tm-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
@click="onClick"
|
||||
:id="id"
|
||||
class="tmSwitch"
|
||||
ref="tmSwitch"
|
||||
:style="{
|
||||
height: boxheight + 'px',
|
||||
width: boxwidth + 'px',
|
||||
backgroundColor: _bgColor,
|
||||
borderRadius: _round,
|
||||
}"
|
||||
>
|
||||
<view
|
||||
:class="[_disabled ? 'tmSwitchDisabled' : '', opened ? 'tmSwitchBgOn' : 'tmSwitchOff']"
|
||||
class="tmSwitchBg"
|
||||
:style="{ backgroundColor: _activeBgColor, borderRadius: _round, 'transition-timing-function': _animationFun }"
|
||||
>
|
||||
</view>
|
||||
<view
|
||||
:class="[_disabled ? 'tmSwitchDisabled' : '']"
|
||||
class="tmSwitchWrap"
|
||||
:style="{
|
||||
left: _space + 'px',
|
||||
top: _space + 'px',
|
||||
width: _contentWidth + 'px',
|
||||
height: _contentHeight + 'px',
|
||||
borderRadius: _round,
|
||||
}"
|
||||
>
|
||||
<view v-if="_label.length == 2" class="tmSwitchText">
|
||||
<text class="tmSwitchTextLeft" :style="{ fontSize: _fontSize }">{{ !opened ? "" : _label[0] }}</text>
|
||||
<text class="tmSwitchTextRight" :style="{ fontSize: _fontSize }">{{ opened ? "" : _label[1] }}</text>
|
||||
</view>
|
||||
<view
|
||||
class="tmSwitchBtn"
|
||||
:style="{
|
||||
width: _contentHeight + 'px',
|
||||
height: _contentHeight + 'px',
|
||||
transform: `translateX(${opened ? _maxLeftPos : 0}px)`,
|
||||
backgroundColor: _btnColor,
|
||||
borderRadius: _round,
|
||||
'transition-timing-function': _animationFun,
|
||||
}"
|
||||
>
|
||||
<tm-icon v-if="_loading" :spin="true" size="24" name="loader-line" :color="_activeBgColor"></tm-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tmSwitchDisabled {
|
||||
opacity: 0.7;
|
||||
cursor: no-drop !important;
|
||||
overflow: hidden;
|
||||
opacity: 0.7;
|
||||
cursor: no-drop !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tmSwitchTextLeft {
|
||||
color: white;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.tmSwitchTextRight {
|
||||
color: #acacac;
|
||||
color: #acacac;
|
||||
}
|
||||
|
||||
.tmSwitch {
|
||||
height: 64rpx;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
height: 64rpx;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tmSwitchBg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition-duration: 350ms;
|
||||
transition-property: opacity, transform;
|
||||
box-sizing: border-box;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition-duration: 350ms;
|
||||
transition-property: opacity, transform;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tmSwitchBgOn {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.tmSwitchOff {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.tmSwitchWrap {
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.tmSwitchText {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 12%;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 12%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tmSwitchBtn {
|
||||
position: absolute;
|
||||
transition-duration: 350ms;
|
||||
left: 0px;
|
||||
transition-property: transform;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
top: 0px;
|
||||
position: absolute;
|
||||
transition-duration: 350ms;
|
||||
left: 0px;
|
||||
transition-property: transform;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
top: 0px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
Reference in new issue
Block a user