Commit fbc59c076058c34e76e8e03cfef60badb0c8b1c0
Merge branch 'main_dev' into 'main'
Main dev See merge request yunteng/thingskit-app!159
Showing
17 changed files
with
2100 additions
and
216 deletions
@@ -4,37 +4,47 @@ | @@ -4,37 +4,47 @@ | ||
4 | * data ((deviceProfileIds)) | 4 | * data ((deviceProfileIds)) |
5 | */ | 5 | */ |
6 | const getDeviceApi = (urlParams, data) => { | 6 | const getDeviceApi = (urlParams, data) => { |
7 | - const { | ||
8 | - page, | ||
9 | - pageSize, | ||
10 | - } = urlParams | ||
11 | - return uni.$u.http.post(`/yt/device?page=${page}&pageSize=${pageSize}`, data); | ||
12 | -}; | 7 | + const { page, pageSize } = urlParams |
8 | + return uni.$u.http.post(`/yt/device?page=${page}&pageSize=${pageSize}`, data) | ||
9 | +} | ||
13 | 10 | ||
14 | // 设备详情 | 11 | // 设备详情 |
15 | const getDeviceDetail = (id) => { | 12 | const getDeviceDetail = (id) => { |
16 | - return uni.$u.http.get(`/yt/device/${id}`); | ||
17 | -}; | 13 | + return uni.$u.http.get(`/yt/device/${id}`) |
14 | +} | ||
18 | 15 | ||
19 | //设备属性 | 16 | //设备属性 |
20 | const getAttribute = (deviceProfileId) => { | 17 | const getAttribute = (deviceProfileId) => { |
21 | - return uni.$u.http.get(`/yt/device/attributes/${deviceProfileId}`); | ||
22 | -}; | 18 | + return uni.$u.http.get(`/yt/device/attributes/${deviceProfileId}`) |
19 | +} | ||
23 | 20 | ||
24 | //命令下发 | 21 | //命令下发 |
25 | const issueCommand = (type, tbDeviceId, data) => { | 22 | const issueCommand = (type, tbDeviceId, data) => { |
26 | - return uni.$u.http.post(`/rpc/${type==='OneWay'?'oneway':'twoway'}/${tbDeviceId}`, data) | 23 | + return uni.$u.http.post(`/rpc/${type === 'OneWay' ? 'oneway' : 'twoway'}/${tbDeviceId}`, data) |
27 | } | 24 | } |
28 | 25 | ||
29 | //获取命令下发记录 | 26 | //获取命令下发记录 |
30 | const getRpcRecord = (params) => { | 27 | const getRpcRecord = (params) => { |
31 | - return uni.$u.http.get('/yt/rpc', params); | ||
32 | -}; | 28 | + return uni.$u.http.get('/yt/rpc', params) |
29 | +} | ||
30 | + | ||
31 | +// 获取设备状态 在线or离线 | ||
32 | +const getDeviceActiveTime = (entityId) => { | ||
33 | + return uni.$u.http.get(`/plugins/telemetry/DEVICE/${entityId}/values/attributes?keys=active`) | ||
34 | +} | ||
35 | + | ||
36 | +// 获取服务调用 | ||
37 | +const getModelServices = (params) => { | ||
38 | + const { deviceProfileId } = params | ||
39 | + return uni.$u.http.get(`/yt/things_model/get_services/${deviceProfileId}`) | ||
40 | +} | ||
33 | 41 | ||
34 | export default { | 42 | export default { |
35 | - getDeviceApi, | ||
36 | - getDeviceDetail, | ||
37 | - getAttribute, | ||
38 | - issueCommand, | ||
39 | - getRpcRecord | ||
40 | -} | ||
43 | + getDeviceApi, | ||
44 | + getDeviceDetail, | ||
45 | + getAttribute, | ||
46 | + issueCommand, | ||
47 | + getRpcRecord, | ||
48 | + getModelServices, | ||
49 | + getDeviceActiveTime, | ||
50 | +} |
@@ -17,15 +17,11 @@ | @@ -17,15 +17,11 @@ | ||
17 | </view> | 17 | </view> |
18 | </view> | 18 | </view> |
19 | <!-- 命令下发 设备在线并且不是网关子设备 --> | 19 | <!-- 命令下发 设备在线并且不是网关子设备 --> |
20 | - <view class="mr-2" v-if="deviceDetail.deviceState === 'ONLINE' && deviceDetail.transportType!==deviceTypeNum.GBT"> | ||
21 | - <!-- #ifdef MP --> | ||
22 | - <u-button type="primary" shape="circle" size="mini" text="下发命令" @click="handleMpShowModal" /> | ||
23 | - <!-- #endif --> | ||
24 | - <!-- #ifdef APP-PLUS --> | 20 | + <view class="mr-2" |
21 | + v-if="deviceDetail.deviceState === 'ONLINE' && deviceDetail.transportType !== deviceTypeNum.GBT"> | ||
25 | <view class="cu-item" @tap="handleAppShowModal" data-target="Modal"> | 22 | <view class="cu-item" @tap="handleAppShowModal" data-target="Modal"> |
26 | - <text>下发命令</text> | 23 | + <text>命令下发</text> |
27 | </view> | 24 | </view> |
28 | - <!-- #endif --> | ||
29 | </view> | 25 | </view> |
30 | </view> | 26 | </view> |
31 | <!-- 设备详情 --> | 27 | <!-- 设备详情 --> |
@@ -61,66 +57,37 @@ | @@ -61,66 +57,37 @@ | ||
61 | </view> | 57 | </view> |
62 | </view> | 58 | </view> |
63 | <!-- 命令下发 --> | 59 | <!-- 命令下发 --> |
64 | - <!-- #ifdef APP-PLUS --> | ||
65 | - <!-- 原生弹窗 封装成子组件无效 --> | ||
66 | - <view v-show="showNativeModal" class="cu-modal" :class="modalName == 'Modal' ? 'show' : ''"> | ||
67 | - <view class="cu-dialog"> | ||
68 | - <view class="app-command-content"> | ||
69 | - <view class="app-command-text"> | ||
70 | - <text>命令下发</text> | ||
71 | - </view> | ||
72 | - <view class="app-command-type"> | ||
73 | - <text>下发类型</text> | ||
74 | - <view class="mr-2"> | ||
75 | - <radio-group @change="radioChange" class="flex mr-1"> | ||
76 | - <label v-for="(item, index) in commandTypeList" :key="item.value"> | ||
77 | - <view class="flex"> | ||
78 | - <view class="ml-1"> | ||
79 | - <radio :value="item.value" :checked="index === current" /> | ||
80 | - </view> | ||
81 | - <view style="width:10rpx"></view> | ||
82 | - <view class="ml-1">{{item.name}}</view> | ||
83 | - </view> | ||
84 | - </label> | ||
85 | - </radio-group> | ||
86 | - </view> | ||
87 | - </view> | ||
88 | - <view class="app-command-body"> | ||
89 | - <textarea class="app-command-textarea" v-model="inputCommandContent" | ||
90 | - :placeholder="`请输入下发内容${isShowTCP?'(字符串格式)':'(json格式)'}`" /> | ||
91 | - <u-icon @click="handleCopy(copyTextValue)" v-if="!isShowTCP" name="question-circle" | ||
92 | - color="#2979ff" size="28" class="ml-10"> | ||
93 | - </u-icon> | ||
94 | - </view> | ||
95 | - <view class="app-command-buttons"> | ||
96 | - <view class="cancel-button" @click="cancelCommand"><text class="cancel-text">取消</text></view> | ||
97 | - <view @click="handleAppCommand" class="confrim-button"><text class="confrim-text">确认</text> | ||
98 | - </view> | ||
99 | - </view> | ||
100 | - </view> | ||
101 | - </view> | ||
102 | - </view> | ||
103 | - <!-- #endif --> | ||
104 | - <!-- #ifdef MP --> | ||
105 | - <!-- u-modal在app端弹窗层级无法覆盖背景色 --> | ||
106 | - <mp-command-issuance ref="mpCommandIssuanceRef" :isShowTCP="isShowTCP" :showModal="mpShowModal" | ||
107 | - @hideModal="hideMpModal" @cancelCommand="cancelCommand" | ||
108 | - @confirmCommand="confirmCommand"></mp-command-issuance> | ||
109 | - <!-- #endif --> | 60 | + <!-- 引入三方库,进行mp端和app端同步弹窗 --> |
61 | + <tn-popup v-model="showNativeModal"> | ||
62 | + <commandIssuanceVue ref="mpCommandIssuanceRef" :isShowTCP="isShowTCP" | ||
63 | + :deviceDetail="deviceDetail" @hideModal="hideMpModal" @cancelCommand="cancelCommand" | ||
64 | + @confirmCommand="confirmCommand" /> | ||
65 | + </tn-popup> | ||
110 | </view> | 66 | </view> |
111 | </template> | 67 | </template> |
112 | 68 | ||
113 | <script> | 69 | <script> |
114 | - import {formatToDate} from '@/plugins/utils.js'; | 70 | + import { |
71 | + formatToDate | ||
72 | + } from '@/plugins/utils.js'; | ||
115 | import api from '@/api/index.js'; | 73 | import api from '@/api/index.js'; |
116 | import mpCommandIssuance from './mp-command-issuance.vue'; | 74 | import mpCommandIssuance from './mp-command-issuance.vue'; |
117 | - import {commandTypeList} from '../config/data.js' | ||
118 | - import {useShowModal} from '@/plugins/utils.js' | ||
119 | - import {deviceTypeNum} from '../config/data' | ||
120 | - | 75 | + import { |
76 | + commandTypeList | ||
77 | + } from '../config/data.js' | ||
78 | + import { | ||
79 | + useShowModal | ||
80 | + } from '@/plugins/utils.js' | ||
81 | + import { | ||
82 | + deviceTypeNum | ||
83 | + } from '../config/data' | ||
84 | + import deviceDetail from '../device-detail.vue'; | ||
85 | + import commandIssuanceVue from './command-issuance.vue'; | ||
86 | + | ||
121 | export default { | 87 | export default { |
122 | components: { | 88 | components: { |
123 | mpCommandIssuance, | 89 | mpCommandIssuance, |
90 | + commandIssuanceVue | ||
124 | }, | 91 | }, |
125 | props: { | 92 | props: { |
126 | deviceDetail: { | 93 | deviceDetail: { |
@@ -133,7 +100,7 @@ | @@ -133,7 +100,7 @@ | ||
133 | showNativeModal: false, | 100 | showNativeModal: false, |
134 | current: 0, | 101 | current: 0, |
135 | commandTypeList, | 102 | commandTypeList, |
136 | - deviceTypeNum:deviceTypeNum, | 103 | + deviceTypeNum: deviceTypeNum, |
137 | commandTypeStr: 'OneWay', | 104 | commandTypeStr: 'OneWay', |
138 | inputCommandContent: '', | 105 | inputCommandContent: '', |
139 | mpShowModal: false, | 106 | mpShowModal: false, |
@@ -233,8 +200,8 @@ | @@ -233,8 +200,8 @@ | ||
233 | this.modalName = null; | 200 | this.modalName = null; |
234 | this.showNativeModal = false | 201 | this.showNativeModal = false |
235 | }, | 202 | }, |
236 | - confirmCommand(commandType, inputCommandVal) { | ||
237 | - this.handleCommand(commandType, inputCommandVal) | 203 | + confirmCommand(commandType, callType, values) { |
204 | + this.handleCommand(commandType, callType, values) | ||
238 | }, | 205 | }, |
239 | cancelCommand() { | 206 | cancelCommand() { |
240 | this.hideMpModal(); | 207 | this.hideMpModal(); |
@@ -246,31 +213,41 @@ | @@ -246,31 +213,41 @@ | ||
246 | }) | 213 | }) |
247 | }, | 214 | }, |
248 | handleAppCommand() { | 215 | handleAppCommand() { |
249 | - this.handleCommand(this.commandTypeStr, this.inputCommandContent) | 216 | + this.handleCommand(this.commandTypeStr, this.commandTypeStr, this.inputCommandContent) |
250 | }, | 217 | }, |
251 | - async handleCommand(commandType, inputCommandVal) { | ||
252 | - if (!inputCommandVal) return uni.$u.toast('请输入下发内容~'); | ||
253 | - if (this.isShowTCP) { | ||
254 | - //TCP的格式只能是字符串 | ||
255 | - const zg = /^[0-9a-zA-Z]*$/; | ||
256 | - if (!zg.test(inputCommandVal)) { | ||
257 | - uni.$u.toast('输入的内容只能是字母和数字的组合'); | ||
258 | - return; | 218 | + async handleCommand(commandType, callType, values) { |
219 | + if (!values) return uni.$u.toast('请输入下发内容~'); | ||
220 | + if (callType == 'TwoWay') { | ||
221 | + const result = await api.deviceApi.getDeviceActiveTime(this.deviceDetail.tbDeviceId) | ||
222 | + const [firsetItem] = result || [] | ||
223 | + if (!firsetItem.value) { | ||
224 | + return uni.$u.toast('当前设备不在线~') | ||
259 | } | 225 | } |
260 | - this.commandValue.params = inputCommandVal; | ||
261 | - } else { | ||
262 | - this.commandValue.params = JSON.parse(inputCommandVal); | ||
263 | } | 226 | } |
227 | + | ||
264 | this.commandValue.persistent = true; | 228 | this.commandValue.persistent = true; |
265 | this.commandValue.additionalInfo = { | 229 | this.commandValue.additionalInfo = { |
266 | - cmdType: 'API' | 230 | + cmdType: commandType == 0 ? 0 : 1 |
267 | }; | 231 | }; |
268 | this.commandValue.method = 'methodThingskit'; | 232 | this.commandValue.method = 'methodThingskit'; |
269 | - this.commandValue.params = inputCommandVal; | ||
270 | - await api.deviceApi.issueCommand(commandType, this.deviceDetail.tbDeviceId, this.commandValue); | 233 | + this.commandValue.params = values; |
234 | + if (commandType == 0) { //下发类型是自定义时 | ||
235 | + if (this.isShowTCP) { | ||
236 | + //TCP的格式只能是字符串 | ||
237 | + const zg = /^[0-9a-zA-Z]*$/; | ||
238 | + if (!zg.test(values)) { | ||
239 | + uni.$u.toast('输入的内容只能是字母和数字的组合'); | ||
240 | + return; | ||
241 | + } | ||
242 | + this.commandValue.params = values; | ||
243 | + } else { | ||
244 | + this.commandValue.params = JSON.parse(values); | ||
245 | + } | ||
246 | + } | ||
247 | + | ||
248 | + await api.deviceApi.issueCommand(callType, this.deviceDetail.tbDeviceId, this.commandValue); | ||
271 | this.cancelCommand(); | 249 | this.cancelCommand(); |
272 | uni.$u.toast('下发成功~'); | 250 | uni.$u.toast('下发成功~'); |
273 | - | ||
274 | }, | 251 | }, |
275 | 252 | ||
276 | } | 253 | } |
@@ -415,4 +392,4 @@ | @@ -415,4 +392,4 @@ | ||
415 | /deep/ .u-modal__content { | 392 | /deep/ .u-modal__content { |
416 | padding: 30rpx 0 !important; | 393 | padding: 30rpx 0 !important; |
417 | } | 394 | } |
418 | -</style> | 395 | +</style> |
1 | +<template> | ||
2 | + <view class="w-100 modal-content"> | ||
3 | + <view> | ||
4 | + <view style="max-height: 560rpx; overflow-y: scroll"> | ||
5 | + <view class="header-title">命令下发</view> | ||
6 | + <view class="u-flex"> | ||
7 | + <text class="type-text">下发类型:</text> | ||
8 | + <u-radio-group v-model="commandType" placement="row" @change="handleCommand"> | ||
9 | + <u-radio :customStyle="{ marginRight: '20rpx' }" v-for="item in commandTypeList" | ||
10 | + activeColor="#3388FF" :label="item.label" :name="item.value" :key="item.value"></u-radio> | ||
11 | + </u-radio-group> | ||
12 | + </view> | ||
13 | + <view class="u-flex" style="margin-top: 28rpx" v-if="commandType == 0"> | ||
14 | + <text class="type-text">单向/双向:</text> | ||
15 | + <u-radio-group v-model="callType" placement="row"> | ||
16 | + <u-radio activeColor="#3388FF" label="单向" name="OneWay"></u-radio> | ||
17 | + <view style="margin: 0 20rpx"></view> | ||
18 | + <u-radio activeColor="#3388FF" label="双向" name="TwoWay"></u-radio> | ||
19 | + </u-radio-group> | ||
20 | + </view> | ||
21 | + <view class="u-flex" style="margin-top: 28rpx" v-else> | ||
22 | + <text class="type-text">服务:</text> | ||
23 | + <view @click="openService"> | ||
24 | + <u-input shape="circle" v-model="serviceName" placeholder="请选择服务" disabled disabledColor="#fff" | ||
25 | + suffixIcon="arrow-down" /> | ||
26 | + </view> | ||
27 | + </view> | ||
28 | + <view class="u-flex" style=" | ||
29 | + margin-top: 28rpx; | ||
30 | + flex-direction: column; | ||
31 | + align-items: flex-start; | ||
32 | + " v-if="isShowServiceFunctionName && commandType == 1"> | ||
33 | + <text class="type-text">输入参数:</text> | ||
34 | + <seriesForm ref="seriesFormRef" :seriesInputData="seriesInputData" :isTCPTransport="isTCPTransport"> | ||
35 | + </seriesForm> | ||
36 | + </view> | ||
37 | + <view class="content-body" v-if="commandType == 0"> | ||
38 | + <div class="u-flex u-row-between"> | ||
39 | + <u--textarea :placeholder="`请输入下发内容${ | ||
40 | + isShowTCP ? '(字符串格式)' : '(json格式)' | ||
41 | + }`" v-model="inputCommandVal" /> | ||
42 | + <u-icon v-if="!isShowTCP" @click="handleCopy(copyTextValue)" name="question-circle" | ||
43 | + color="#2979ff" size="28" class="ml-10"> | ||
44 | + </u-icon> | ||
45 | + </div> | ||
46 | + </view> | ||
47 | + </view> | ||
48 | + <view class="button-group"> | ||
49 | + <view> | ||
50 | + <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="取消" | ||
51 | + @click="cancelCommand"></u-button> | ||
52 | + </view> | ||
53 | + <view> | ||
54 | + <u-button color="#3388ff" shape="circle" text="确认" @click="confirmCommand"></u-button> | ||
55 | + </view> | ||
56 | + </view> | ||
57 | + </view> | ||
58 | + <u-picker :show="isShowService" :columns="[ | ||
59 | + seriesList.map((item) => ({ | ||
60 | + label: item.functionName, | ||
61 | + value: item.identifier, | ||
62 | + callType: item.callType, | ||
63 | + })), | ||
64 | + ]" keyName="label" closeOnClickOverlay @cancel="cancelTypeGap" @close="cancelTypeGap" | ||
65 | + @confirm="handleSelect"></u-picker> | ||
66 | + </view> | ||
67 | +</template> | ||
68 | + | ||
69 | +<script> | ||
70 | + import { | ||
71 | + useShowModal | ||
72 | + } from "@/plugins/utils.js"; | ||
73 | + import seriesForm from "./seriesForm.vue"; | ||
74 | + import api from "@/api/index.js"; | ||
75 | + | ||
76 | + export default { | ||
77 | + components: { | ||
78 | + seriesForm, | ||
79 | + }, | ||
80 | + props: { | ||
81 | + showModal: Boolean, | ||
82 | + isShowTCP: Boolean, | ||
83 | + deviceDetail: Object, | ||
84 | + }, | ||
85 | + data() { | ||
86 | + return { | ||
87 | + current: 0, | ||
88 | + commandType: 0, //下发类型 | ||
89 | + callType: "OneWay", //单双向 | ||
90 | + serviceName: "", //服务 | ||
91 | + service: null, //服务 | ||
92 | + inputCommandVal: "", //自定义命令 | ||
93 | + isShowService: false, //服务下拉框 | ||
94 | + isShowServiceFunctionName: false, //选择服务过后的输入参数 | ||
95 | + copyTextValue: { | ||
96 | + method: "methodThingskit", | ||
97 | + params: { | ||
98 | + pin: 7, | ||
99 | + value: 1, | ||
100 | + }, | ||
101 | + }, | ||
102 | + commandTypeList: [{ | ||
103 | + label: "自定义", | ||
104 | + value: 0 | ||
105 | + }], | ||
106 | + seriesList: [], //服务下拉框的数据 | ||
107 | + boolList: [], | ||
108 | + enumList: [], | ||
109 | + seriesInputData: [], | ||
110 | + isTCPTransport: false, | ||
111 | + }; | ||
112 | + }, | ||
113 | + mounted() { | ||
114 | + this.getFormInfo(); | ||
115 | + }, | ||
116 | + watch: { | ||
117 | + showModal: { | ||
118 | + deep: true, | ||
119 | + handler() { | ||
120 | + this.commandType = 0; | ||
121 | + this.serviceName = ""; | ||
122 | + this.isShowServiceFunctionName = false; | ||
123 | + }, | ||
124 | + }, | ||
125 | + }, | ||
126 | + methods: { | ||
127 | + cancelCommand() { | ||
128 | + this.commandType = 0; | ||
129 | + this.$emit("cancelCommand"); | ||
130 | + }, | ||
131 | + async confirmCommand() { | ||
132 | + if (this.commandType == 0) { | ||
133 | + this.$emit( | ||
134 | + "confirmCommand", | ||
135 | + this.commandType, | ||
136 | + this.callType, | ||
137 | + this.inputCommandVal | ||
138 | + ); | ||
139 | + } else { | ||
140 | + const result = this.$refs.seriesFormRef.handleValidate(); | ||
141 | + if (!result) { | ||
142 | + return; | ||
143 | + } | ||
144 | + const value = this.$refs.seriesFormRef.getFormField(); | ||
145 | + const values = this.isTCPTransport ? | ||
146 | + value.serviceCommand : { | ||
147 | + [this.service]: value, | ||
148 | + }; | ||
149 | + this.$emit("confirmCommand", this.commandType, this.callType, values); | ||
150 | + } | ||
151 | + }, | ||
152 | + handleCopy(value) { | ||
153 | + useShowModal(JSON.stringify(value), "命令下发", "复制内容").then( | ||
154 | + (res) => { | ||
155 | + uni.setClipboardData({ | ||
156 | + data: JSON.stringify(value), | ||
157 | + success: () => { | ||
158 | + uni.showToast({ | ||
159 | + title: "复制成功", | ||
160 | + }); | ||
161 | + }, | ||
162 | + }); | ||
163 | + } | ||
164 | + ); | ||
165 | + }, | ||
166 | + | ||
167 | + async getFormInfo() { | ||
168 | + const { | ||
169 | + transportType, | ||
170 | + deviceType, | ||
171 | + deviceProfile | ||
172 | + } = | ||
173 | + this.deviceDetail || {}; | ||
174 | + const { | ||
175 | + profileData: { | ||
176 | + transportConfiguration: { | ||
177 | + protocol | ||
178 | + }, | ||
179 | + }, | ||
180 | + } = deviceProfile || {}; | ||
181 | + this.isTCPTransport = transportType === "TCP"; | ||
182 | + const isTCPModbus = this.isTCPTransport && protocol === "MODBUS_RTU"; | ||
183 | + if (isTCPModbus || (this.isTCPTransport && deviceType === "SENSOR")) { | ||
184 | + this.commandTypeList = | ||
185 | + this.commandTypeList.length == 2 ? | ||
186 | + this.commandTypeList.pop() : | ||
187 | + this.commandTypeList; | ||
188 | + } else { | ||
189 | + this.commandTypeList.push({ | ||
190 | + label: "服务", | ||
191 | + value: 1 | ||
192 | + }); | ||
193 | + this.seriesList = await api.deviceApi.getModelServices( | ||
194 | + this.deviceDetail | ||
195 | + ); | ||
196 | + } | ||
197 | + }, | ||
198 | + | ||
199 | + openService() { | ||
200 | + this.isShowService = true; | ||
201 | + }, | ||
202 | + handleSelect(e) { | ||
203 | + this.isShowService = false; | ||
204 | + const { | ||
205 | + value | ||
206 | + } = e || {}; | ||
207 | + this.serviceName = value[0].label; | ||
208 | + this.service = value[0].value; | ||
209 | + if (this.service) { | ||
210 | + this.isShowServiceFunctionName = true; | ||
211 | + const { | ||
212 | + functionJson | ||
213 | + } = | ||
214 | + this.seriesList.filter( | ||
215 | + (item) => item.identifier === this.service | ||
216 | + )[0] || {}; | ||
217 | + const { | ||
218 | + inputData | ||
219 | + } = functionJson || {}; | ||
220 | + this.seriesInputData = inputData || []; | ||
221 | + this.callType = value[0].callType === "ASYNC" ? "OneWay" : "TwoWay"; | ||
222 | + } | ||
223 | + }, | ||
224 | + | ||
225 | + cancelModel() { | ||
226 | + this.isShowService = false; | ||
227 | + this.seriesInputData = []; | ||
228 | + this.seriesList = []; | ||
229 | + }, | ||
230 | + | ||
231 | + handleCommand(name) { | ||
232 | + this.seriesInputData = []; | ||
233 | + this.serviceName = ""; | ||
234 | + this.isShowServiceFunctionName = false; | ||
235 | + if (this.commandType == 0) { | ||
236 | + this.callType = "OneWay"; | ||
237 | + } | ||
238 | + }, | ||
239 | + | ||
240 | + cancelTypeGap() { | ||
241 | + this.isShowService = false; | ||
242 | + }, | ||
243 | + | ||
244 | + reset() { | ||
245 | + this.callType = "OneWay"; | ||
246 | + this.inputCommandVal = ""; | ||
247 | + }, | ||
248 | + }, | ||
249 | + }; | ||
250 | +</script> | ||
251 | + | ||
252 | +<style lang="scss" scoped> | ||
253 | + .modal-content { | ||
254 | + width: 720rpx; | ||
255 | + padding: 0 30rpx; | ||
256 | + background-color: white; | ||
257 | + | ||
258 | + .header-title { | ||
259 | + text-align: center; | ||
260 | + font-weight: 700; | ||
261 | + margin-bottom: 40rpx; | ||
262 | + } | ||
263 | + | ||
264 | + .type-text { | ||
265 | + color: #333; | ||
266 | + font-size: 14px; | ||
267 | + font-weight: 700; | ||
268 | + margin-right: 30rpx; | ||
269 | + } | ||
270 | + | ||
271 | + .content-body { | ||
272 | + margin-top: 28rpx; | ||
273 | + width: 100%; | ||
274 | + } | ||
275 | + | ||
276 | + .button-group { | ||
277 | + display: flex; | ||
278 | + margin-top: 40rpx; | ||
279 | + justify-content: space-between; | ||
280 | + | ||
281 | + view { | ||
282 | + width: 300rpx; | ||
283 | + } | ||
284 | + } | ||
285 | + } | ||
286 | +</style> |
1 | <template> | 1 | <template> |
2 | <view class="mp-u-modal"> | 2 | <view class="mp-u-modal"> |
3 | <u-modal :mask-close-able="true" :show="showModal" closeOnClickOverlay :showConfirmButton="false" | 3 | <u-modal :mask-close-able="true" :show="showModal" closeOnClickOverlay :showConfirmButton="false" |
4 | - @close="$emit('hideModal')" @touchmove.stop.prevent="disabledScroll" z-index="99999"> | ||
5 | - <view class="w-100 modal-content"> | ||
6 | - <view class="header-title">命令下发</view> | ||
7 | - <view class="u-flex"> | ||
8 | - <text class="type-text">下发类型:</text> | ||
9 | - <u-radio-group v-model="commandType" placement="row"> | ||
10 | - <u-radio activeColor="#3388FF" label="单向" name="OneWay"></u-radio> | ||
11 | - <view style="margin: 0 20rpx;"></view> | ||
12 | - <u-radio activeColor="#3388FF" label="双向" name="TwoWay"></u-radio> | ||
13 | - </u-radio-group> | ||
14 | - </view> | ||
15 | - <view class="content-body"> | ||
16 | - <div class="u-flex u-row-between"> | ||
17 | - <u--textarea :placeholder="`请输入下发内容${isShowTCP?'(字符串格式)':'(json格式)'}`" | ||
18 | - v-model="inputCommandVal" /> | ||
19 | - <u-icon v-if="!isShowTCP" @click="handleCopy(copyTextValue)" name="question-circle" | ||
20 | - color="#2979ff" size="28" class="ml-10"> | ||
21 | - </u-icon> | ||
22 | - </div> | ||
23 | - </view> | ||
24 | - <view class="button-group"> | ||
25 | - <view> | ||
26 | - <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="取消" | ||
27 | - @click="cancelCommand"></u-button> | ||
28 | - </view> | ||
29 | - <view> | ||
30 | - <u-button color="#3388ff" shape="circle" text="确认" @click="confirmCommand"></u-button> | ||
31 | - </view> | ||
32 | - </view> | ||
33 | - </view> | 4 | + @close="$emit('hideModal')" z-index="99999"> |
5 | + <commandIssuance :isShowTCP="isShowTCP" :deviceDetail="deviceDetail" /> | ||
34 | </u-modal> | 6 | </u-modal> |
35 | </view> | 7 | </view> |
36 | </template> | 8 | </template> |
37 | 9 | ||
38 | <script> | 10 | <script> |
39 | - import { | ||
40 | - useShowModal | ||
41 | - } from '@/plugins/utils.js' | 11 | + import commandIssuance from "./command-issuance.vue"; |
42 | 12 | ||
43 | export default { | 13 | export default { |
14 | + components: { | ||
15 | + commandIssuance, | ||
16 | + }, | ||
44 | props: { | 17 | props: { |
45 | showModal: Boolean, | 18 | showModal: Boolean, |
46 | - isShowTCP: Boolean | ||
47 | - }, | ||
48 | - data() { | ||
49 | - return { | ||
50 | - current: 0, | ||
51 | - commandType: 'OneWay', | ||
52 | - inputCommandVal: '', | ||
53 | - copyTextValue: { | ||
54 | - "method": "methodThingskit", | ||
55 | - "params": { | ||
56 | - "pin": 7, | ||
57 | - "value": 1 | ||
58 | - } | ||
59 | - } | ||
60 | - } | 19 | + isShowTCP: Boolean, |
20 | + deviceDetail: Object, | ||
61 | }, | 21 | }, |
62 | - methods: { | ||
63 | - cancelCommand() { | ||
64 | - this.$emit('cancelCommand') | ||
65 | - }, | ||
66 | - confirmCommand() { | ||
67 | - this.$emit('confirmCommand', this.commandType, this.inputCommandVal) | ||
68 | - }, | ||
69 | - handleCopy(value) { | ||
70 | - useShowModal(JSON.stringify(value), '命令下发', '复制内容').then(res => { | ||
71 | - uni.setClipboardData({ | ||
72 | - data: JSON.stringify(value), | ||
73 | - success: () => { | ||
74 | - uni.showToast({ | ||
75 | - title: '复制成功' | ||
76 | - }) | ||
77 | - } | ||
78 | - }); | ||
79 | - }) | ||
80 | - }, | ||
81 | - reset() { | ||
82 | - this.commandType = 'OneWay' | ||
83 | - this.inputCommandVal = '' | ||
84 | - } | ||
85 | - } | ||
86 | - } | 22 | + }; |
87 | </script> | 23 | </script> |
88 | 24 | ||
89 | -<style lang="scss" scoped> | ||
90 | - .modal-content { | ||
91 | - width: 720rpx; | ||
92 | - padding: 0 30rpx; | ||
93 | - background-color: white; | ||
94 | - | ||
95 | - .header-title { | ||
96 | - text-align: center; | ||
97 | - font-weight: 700; | ||
98 | - margin-bottom: 40rpx; | ||
99 | - } | ||
100 | - | ||
101 | - .type-text { | ||
102 | - color: #333; | ||
103 | - font-size: 14px; | ||
104 | - font-weight: 700; | ||
105 | - margin-right: 30rpx; | ||
106 | - } | ||
107 | - | ||
108 | - .content-body { | ||
109 | - margin-top: 28rpx; | ||
110 | - width: 100%; | ||
111 | - } | ||
112 | - | ||
113 | - .button-group { | ||
114 | - display: flex; | ||
115 | - margin-top: 40rpx; | ||
116 | - justify-content: space-between; | ||
117 | - | ||
118 | - view { | ||
119 | - width: 300rpx; | ||
120 | - } | ||
121 | - } | ||
122 | - } | ||
123 | -</style> | ||
25 | +<style lang="scss" scoped></style> |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | <view>{{ item.name }}</view> | 5 | <view>{{ item.name }}</view> |
6 | <view class="item-value">{{ item.value || '' }}</view> | 6 | <view class="item-value">{{ item.value || '' }}</view> |
7 | </view> | 7 | </view> |
8 | - <view class="item-time">{{ item.time }}</view> | 8 | + <view class="item-time">{{ item.value? item.time:'' }}</view> |
9 | </view> | 9 | </view> |
10 | <mescroll-empty v-if="!recordList.length" /> | 10 | <mescroll-empty v-if="!recordList.length" /> |
11 | </view> | 11 | </view> |
1 | +<template> | ||
2 | + <view> | ||
3 | + <!-- :label-style="{'width':'120rpx','overflow':'hidden','text-overflow':'ellipsis','white-space':'nowrap',display:'block'}" label-width="120rpx" --> | ||
4 | + <u-form :model="seriesForm" ref="seriesRef"> | ||
5 | + <u-form-item style="display: flex" v-for="(item, index) in seriesFunctionList" label-position="left" :key="item.identifier" :prop="item.identifier"> | ||
6 | + <view v-if="!isTCPTransport"" :class="Array.isArray(item.dataType.specs)?'positionTop':'positionLeft'"> | ||
7 | + <view class="text">{{ item.functionName }}</view> | ||
8 | + <u-input | ||
9 | + v-if="item.dataType.type == 'INT' || item.dataType.type == 'DOUBLE'" | ||
10 | + shape="circle" | ||
11 | + type="number" | ||
12 | + v-model.number="seriesForm[item.identifier]" | ||
13 | + :placeholder="`请输入${item.functionName}`" | ||
14 | + @blur="(e)=>handleBlur(e,item.identifier)" | ||
15 | + /> | ||
16 | + <u-input v-if="item.dataType.type == 'TEXT'" shape="circle" type="text" v-model="seriesForm[item.identifier]" :placeholder="`请输入${item.functionName}`" /> | ||
17 | + <view v-if="item.dataType.type == 'BOOL'" @click="handleBool(item.functionName, item.dataType, index)"> | ||
18 | + <u-input | ||
19 | + shape="circle" | ||
20 | + v-model="seriesForm[item.identifier]" | ||
21 | + :placeholder="`请选择${item.functionName}`" | ||
22 | + disabled | ||
23 | + disabledColor="#fff" | ||
24 | + suffixIcon="arrow-down" | ||
25 | + /> | ||
26 | + <u-picker | ||
27 | + :show="showPickerView" | ||
28 | + :columns="[boolList]" | ||
29 | + keyName="label" | ||
30 | + @cancel="handleCancelBool(index)" | ||
31 | + @close="handleCancelBool(index)" | ||
32 | + @confirm="(e) => handleSelectBool(e, item.identifier, index)" | ||
33 | + ></u-picker> | ||
34 | + </view> | ||
35 | + <view v-if="item.dataType.type == 'ENUM'" @click="handleEnum(item.functionName, item.dataType, index)"> | ||
36 | + <u-input | ||
37 | + shape="circle" | ||
38 | + v-model="seriesForm[item.identifier]" | ||
39 | + :placeholder="`请选择${item.functionName}`" | ||
40 | + disabled | ||
41 | + disabledColor="#fff" | ||
42 | + suffixIcon="arrow-down" | ||
43 | + /> | ||
44 | + <u-picker | ||
45 | + :show="showPickerView" | ||
46 | + :columns="[enumList.map((item) => ({ label: item.name, value: item.value }))]" | ||
47 | + keyName="label" | ||
48 | + @cancel="handleCancelEnum(index)" | ||
49 | + @close="handleCancelEnum(index)" | ||
50 | + @confirm="(e) => handleSelectEnum(e, item.identifier, index)" | ||
51 | + ></u-picker> | ||
52 | + </view> | ||
53 | + <template v-if="Array.isArray(item.dataType.specs)"> | ||
54 | + <structuralForm class="seriesForm" :ref="item.identifier" :seriesInputData="item.dataType.specs || []"></structuralForm> | ||
55 | + </template> | ||
56 | + </view> | ||
57 | + <view v-else class="positionLeft" style=" border: 1px dashed #f0f0f0; padding: 20rpx;"> | ||
58 | + <view class="text">服务命令</view> | ||
59 | + <u-input v-model="seriesForm.serviceCommand" type="text" shape="circle" :disabled="true"></u-input> | ||
60 | + </view> | ||
61 | + </u-form-item> | ||
62 | + </u-form> | ||
63 | + </view> | ||
64 | +</template> | ||
65 | + | ||
66 | +<script> | ||
67 | +import structuralForm from './structuralForm.vue' | ||
68 | +export default { | ||
69 | + name: 'StructForm', | ||
70 | + props: { | ||
71 | + seriesInputData: { | ||
72 | + type: Array, | ||
73 | + default: () => [], | ||
74 | + }, | ||
75 | + isTCPTransport:{ | ||
76 | + type:Boolean, | ||
77 | + default:false | ||
78 | + } | ||
79 | + }, | ||
80 | + components: { | ||
81 | + structuralForm, | ||
82 | + }, | ||
83 | + data() { | ||
84 | + return { | ||
85 | + showPickerView:false, | ||
86 | + boolInfo: {}, | ||
87 | + enumInfo: {}, | ||
88 | + seriesForm: {}, | ||
89 | + seriesRules: {}, | ||
90 | + seriesFunctionList: [], | ||
91 | + boolList: [], | ||
92 | + enumList: [], | ||
93 | + } | ||
94 | + }, | ||
95 | + async mounted() { | ||
96 | + await this.$nextTick(() => { | ||
97 | + this.createInit() | ||
98 | + }) | ||
99 | + }, | ||
100 | + watch: { | ||
101 | + seriesInputData: { | ||
102 | + deep: true, | ||
103 | + handler(newVal, oldVal) { | ||
104 | + this.createInit() | ||
105 | + }, | ||
106 | + }, | ||
107 | + }, | ||
108 | + | ||
109 | + methods: { | ||
110 | + createInit() { | ||
111 | + this.seriesForm = {} | ||
112 | + this.boolInfo = {} | ||
113 | + this.seriesFunctionList = this.seriesInputData | ||
114 | + for(const item of this.seriesInputData){ | ||
115 | + this.$set(item, 'isShowModel', false)//动态添加picker的控制变量 | ||
116 | + const { | ||
117 | + dataType, | ||
118 | + identifier, | ||
119 | + functionName, | ||
120 | + serviceCommand | ||
121 | + } = item || {} | ||
122 | + const {specs,type} = dataType || {} | ||
123 | + if(this.isTCPTransport){ | ||
124 | + this.$set(this.seriesForm,'serviceCommand',serviceCommand) | ||
125 | + break; | ||
126 | + } | ||
127 | + if (Array.isArray(specs)) { | ||
128 | + specs.forEach((itemSpecs) => { | ||
129 | + this.$set(this.seriesForm, identifier, { | ||
130 | + [itemSpecs.identifier]: '', | ||
131 | + }) | ||
132 | + }) | ||
133 | + } else { | ||
134 | + this.$set(this.seriesForm, identifier, '') | ||
135 | + } | ||
136 | + | ||
137 | + //设置验证规则 | ||
138 | + const { valueRange, length = 10240 } = specs || {} | ||
139 | + const { max = 2147483647, min = -2147483647 } = valueRange || {} | ||
140 | + if (type !== 'STRUCT') { | ||
141 | + this.seriesRules[identifier] = [ | ||
142 | + { required: true, message: type == 'BOOL' || type == 'ENUM' ? '请选择' : '请输入' + functionName }, | ||
143 | + type == 'INT' || type == 'DOUBLE' | ||
144 | + ? { | ||
145 | + type: 'number', | ||
146 | + trigger: 'change', | ||
147 | + validator: (_rule, value) => { | ||
148 | + const reg = /^[0-9]*$/ | ||
149 | + if (!reg.test(value)) return Promise.reject(new Error(`${functionName}不是一个有效的数字`)) | ||
150 | + if (value < min || value > max) return Promise.reject(new Error(`${functionName}取值范围在${min}~${max}之间`)) | ||
151 | + | ||
152 | + return Promise.resolve(value) | ||
153 | + }, | ||
154 | + } | ||
155 | + : type == 'TEXT' | ||
156 | + ? { | ||
157 | + type: 'string', | ||
158 | + trigger: 'change', | ||
159 | + validator: (_rule, value) => { | ||
160 | + if ((value?.length || 0) > length) return Promise.reject(new Error(`${functionName}数据长度应该小于${length}`)) | ||
161 | + | ||
162 | + return Promise.resolve(value) | ||
163 | + }, | ||
164 | + } | ||
165 | + : {}, | ||
166 | + ] | ||
167 | + } | ||
168 | + } | ||
169 | + //设置验证规则 | ||
170 | + this.$nextTick(() => { | ||
171 | + !this.isTCPTransport && this.$refs.seriesRef.setRules(this.seriesRules) | ||
172 | + }) | ||
173 | + }, | ||
174 | + isEmptyObject(obj) { | ||
175 | + return Object.keys(obj).length === 0 && obj.constructor === Object | ||
176 | + }, | ||
177 | + | ||
178 | + //打开Bool的picker | ||
179 | + handleBool(name, dataType, num) { | ||
180 | + this.seriesFunctionList.forEach((item, index) => { | ||
181 | + if (index == num) { | ||
182 | + this.showPickerView = true | ||
183 | + item.isShowModel = true | ||
184 | + } | ||
185 | + }) | ||
186 | + const { specs } = dataType || {} | ||
187 | + const { boolClose, boolOpen } = specs || {} | ||
188 | + if(!boolClose&&!boolOpen){ | ||
189 | + uni.$u.toast(`暂无可选的${name}`) | ||
190 | + this.boolList = [] | ||
191 | + return | ||
192 | + } | ||
193 | + this.boolList = [ | ||
194 | + { label: boolClose + '-0', value: 0 }, | ||
195 | + { label: boolOpen + '-1', value: 1 }, | ||
196 | + ] | ||
197 | + }, | ||
198 | + handleSelectBool(e, name, num) { | ||
199 | + const { value } = e || {} | ||
200 | + this.boolInfo[name] = value[0].value | ||
201 | + this.$set(this.seriesForm, name, value[0].label) | ||
202 | + this.seriesFunctionList.forEach((item, index) => { | ||
203 | + if (index == num) { | ||
204 | + item.isShowModel = false | ||
205 | + this.showPickerView = false | ||
206 | + } | ||
207 | + }) | ||
208 | + }, | ||
209 | + handleBlur(value,name){ | ||
210 | + if(!value) return | ||
211 | + this.$set(this.seriesForm,name,Number(value)) | ||
212 | + }, | ||
213 | + | ||
214 | + //打开Enum的picker | ||
215 | + handleEnum(name, dataType, num) { | ||
216 | + const { specsList } = dataType || {} | ||
217 | + this.enumList = specsList || [] | ||
218 | + console.log(this.enumList,'enumInfo') | ||
219 | + if(!this.enumList.length){ | ||
220 | + return uni.$u.toast(`暂无可选的${name}`) | ||
221 | + } | ||
222 | + this.seriesFunctionList.forEach((item, index) => { | ||
223 | + if (index == num) { | ||
224 | + item.isShowModel = true | ||
225 | + this.showPickerView = true | ||
226 | + } | ||
227 | + }) | ||
228 | + }, | ||
229 | + //确定选中 | ||
230 | + handleSelectEnum(e, name, num) { | ||
231 | + const { value } = e || {} | ||
232 | + this.enumInfo[name] = value[0].value | ||
233 | + this.$set(this.seriesForm, name, value[0].label) | ||
234 | + this.seriesFunctionList.forEach((item, index) => { | ||
235 | + if (index == num) { | ||
236 | + item.isShowModel = false | ||
237 | + this.showPickerView = false | ||
238 | + } | ||
239 | + }) | ||
240 | + }, | ||
241 | + | ||
242 | + | ||
243 | + //关闭Enum和Bool的picker弹框 | ||
244 | + handleCancelEnum(num){ | ||
245 | + this.seriesFunctionList.forEach((item, index) => { | ||
246 | + if (index == num) { | ||
247 | + item.isShowModel = false | ||
248 | + this.showPickerView = false | ||
249 | + } | ||
250 | + }) | ||
251 | + }, | ||
252 | + handleCancelBool(num){ | ||
253 | + this.seriesFunctionList.forEach((item, index) => { | ||
254 | + if (index == num) { | ||
255 | + item.isShowModel = false | ||
256 | + this.showPickerView = false | ||
257 | + } | ||
258 | + }) | ||
259 | + }, | ||
260 | + | ||
261 | + // 获取表单数据 | ||
262 | + getFormField() { | ||
263 | + const keys = Object.keys(this.seriesForm) | ||
264 | + for (let i = 0; i < keys.length; i++) { | ||
265 | + const key = keys[i] | ||
266 | + if (Array.isArray(this.$refs[key])) { | ||
267 | + const values = this.$refs[key][0]?.getFormField() | ||
268 | + if (!this.isEmptyObject(values)) { | ||
269 | + this.seriesForm[key] = values | ||
270 | + } | ||
271 | + } | ||
272 | + } | ||
273 | + return { | ||
274 | + ...this.seriesForm, | ||
275 | + ...this.boolInfo, | ||
276 | + ...this.enumInfo, | ||
277 | + } | ||
278 | + }, | ||
279 | + | ||
280 | + | ||
281 | + handleValidate() { | ||
282 | + const keys = Object.keys(this.seriesForm) | ||
283 | + for (let i = 0; i < keys.length; i++) { | ||
284 | + const key = keys[i] | ||
285 | + if (Array.isArray(this.$refs[key])) { | ||
286 | + this.$refs[key][0]?.handleValidate(valid=>{ | ||
287 | + if(!valid) return false | ||
288 | + }) | ||
289 | + } else { | ||
290 | + this.$refs.seriesRef.validate(valid=>{ | ||
291 | + if(!valid) return false | ||
292 | + }) | ||
293 | + } | ||
294 | + return true | ||
295 | + } | ||
296 | + }, | ||
297 | + }, | ||
298 | +} | ||
299 | +</script> | ||
300 | + | ||
301 | +<style lang="scss" scoped> | ||
302 | +.positionLeft { | ||
303 | + display: flex; | ||
304 | + align-items: center; | ||
305 | + .text{ | ||
306 | + | ||
307 | + max-width: 190rpx; | ||
308 | + overflow: hidden; | ||
309 | + text-overflow: ellipsis; | ||
310 | + white-space: nowrap; | ||
311 | + } | ||
312 | +} | ||
313 | + | ||
314 | +.positionTop { | ||
315 | + display: flex; | ||
316 | + flex-direction: column; | ||
317 | + border: 1px dashed #f0f0f0; | ||
318 | + padding: 20rpx; | ||
319 | + .seriesForm{ | ||
320 | + margin-left:30rpx | ||
321 | + } | ||
322 | +} | ||
323 | +</style> |
1 | +<template> | ||
2 | + <view> | ||
3 | + <u-form :model="seriesForm" ref="seriesRef" :label-style="{ width: '160rpx', overflow: 'hidden', 'text-overflow': 'ellipsis', 'white-space': 'nowrap', display: 'block' }" label-width="160rpx"> | ||
4 | + <u-form-item v-for="(item, index) in seriesFunctionList" :key="item.identifier" :label="item.functionName" :prop="item.identifier"> | ||
5 | + <u-input v-if="item.dataType.type == 'INT' || item.dataType.type == 'DOUBLE'" shape="circle" type="number" v-model="seriesForm[item.identifier]" :placeholder="`请输入${item.functionName}`" /> | ||
6 | + <u-input v-if="item.dataType.type == 'TEXT'" shape="circle" type="text" v-model="seriesForm[item.identifier]" :placeholder="`请输入${item.functionName}`" /> | ||
7 | + <view @click="handleBool(item.identifier, item.dataType, index)"> | ||
8 | + <u-input | ||
9 | + v-if="item.dataType.type == 'BOOL'" | ||
10 | + shape="circle" | ||
11 | + v-model="seriesForm[item.identifier]" | ||
12 | + :placeholder="`请选择${item.functionName}`" | ||
13 | + disabled | ||
14 | + disabledColor="#fff" | ||
15 | + suffixIcon="arrow-down" | ||
16 | + /> | ||
17 | + <u-picker | ||
18 | + :show="showPickerView" | ||
19 | + :columns="[boolList]" | ||
20 | + keyName="label" | ||
21 | + closeOnClickOverlay | ||
22 | + @cancel="handleCancel(index)" | ||
23 | + @close="handleCancel(index)" | ||
24 | + @confirm="(e) => handleSelectBool(e, item.identifier, index)" | ||
25 | + ></u-picker> | ||
26 | + </view> | ||
27 | + | ||
28 | + <view @click="handleEnum(item.identifier, item.dataType, index)"> | ||
29 | + <u-input | ||
30 | + v-if="item.dataType.type == 'ENUM'" | ||
31 | + shape="circle" | ||
32 | + v-model="seriesForm[item.identifier]" | ||
33 | + :placeholder="`请选择${item.functionName}`" | ||
34 | + disabled | ||
35 | + disabledColor="#fff" | ||
36 | + suffixIcon="arrow-down" | ||
37 | + /> | ||
38 | + <u-picker | ||
39 | + :show="showPickerView" | ||
40 | + :columns="[enumList.map((item) => ({ label: item.name, value: item.value }))]" | ||
41 | + keyName="label" | ||
42 | + closeOnClickOverlay | ||
43 | + @cancel="handleCancel(index)" | ||
44 | + @close="handleCancel(index)" | ||
45 | + @confirm="(e) => handleSelectEnum(e, item.identifier, index)" | ||
46 | + ></u-picker> | ||
47 | + </view> | ||
48 | + </u-form-item> | ||
49 | + </u-form> | ||
50 | + </view> | ||
51 | +</template> | ||
52 | + | ||
53 | +<script> | ||
54 | +export default { | ||
55 | + name: 'StructForm', | ||
56 | + props: { | ||
57 | + seriesInputData: { | ||
58 | + type: Array, | ||
59 | + default: () => [], | ||
60 | + }, | ||
61 | + }, | ||
62 | + data() { | ||
63 | + return { | ||
64 | + showPickerView: false, | ||
65 | + boolInfo: {}, | ||
66 | + enumInfo: {}, | ||
67 | + seriesForm: {}, | ||
68 | + seriesRules: {}, | ||
69 | + seriesFunctionList: [], | ||
70 | + boolList: [], | ||
71 | + enumList: [], | ||
72 | + } | ||
73 | + }, | ||
74 | + async mounted() { | ||
75 | + await this.$nextTick(() => { | ||
76 | + this.createInit() | ||
77 | + }) | ||
78 | + }, | ||
79 | + watch: { | ||
80 | + seriesInputData: { | ||
81 | + deep: true, | ||
82 | + handler(newVal, oldVal) { | ||
83 | + this.createInit() | ||
84 | + }, | ||
85 | + }, | ||
86 | + }, | ||
87 | + | ||
88 | + methods: { | ||
89 | + createInit() { | ||
90 | + this.seriesFunctionList = this.seriesInputData | ||
91 | + this.seriesInputData.forEach((item) => { | ||
92 | + this.$set(item, 'isShowModel', false) | ||
93 | + this.showPickerView=false | ||
94 | + const { | ||
95 | + dataType: { specs, type }, | ||
96 | + identifier, | ||
97 | + functionName, | ||
98 | + } = item || {} | ||
99 | + | ||
100 | + this.$set(this.seriesForm, identifier, '') | ||
101 | + const { valueRange, length = 10240 } = specs || {} | ||
102 | + | ||
103 | + const { max = 2147483647, min = -2147483647 } = valueRange || {} | ||
104 | + | ||
105 | + this.seriesRules[identifier] = [ | ||
106 | + { required: true, message: type == 'BOOL' || type == 'ENUM' ? '请选择' : '请输入' + functionName }, | ||
107 | + type == 'INT' || type == 'DOUBLE' | ||
108 | + ? { | ||
109 | + type: 'number', | ||
110 | + trigger: 'change', | ||
111 | + validator: (_rule, value) => { | ||
112 | + const reg = /^[0-9]*$/ | ||
113 | + if (!reg.test(value)) return Promise.reject(new Error(`${functionName}不是一个有效的数字`)) | ||
114 | + if (value < min || value > max) return Promise.reject(new Error(`${functionName}取值范围在${min}~${max}之间`)) | ||
115 | + | ||
116 | + return Promise.resolve(value) | ||
117 | + }, | ||
118 | + } | ||
119 | + : type == 'TEXT' | ||
120 | + ? { | ||
121 | + type: 'string', | ||
122 | + trigger: 'change', | ||
123 | + validator: (_rule, value) => { | ||
124 | + if ((value?.length || 0) > length) return Promise.reject(new Error(`${functionName}数据长度应该小于${length}`)) | ||
125 | + | ||
126 | + return Promise.resolve(value) | ||
127 | + }, | ||
128 | + } | ||
129 | + : {}, | ||
130 | + ] | ||
131 | + }) | ||
132 | + this.$nextTick(() => { | ||
133 | + this.$refs.seriesRef.setRules(this.seriesRules) | ||
134 | + }) | ||
135 | + }, | ||
136 | + | ||
137 | + handleBool(name, dataType, num) { | ||
138 | + this.seriesFunctionList.forEach((item, index) => { | ||
139 | + if (index == num) { | ||
140 | + item.isShowModel = true | ||
141 | + this.showPickerView=true | ||
142 | + } | ||
143 | + }) | ||
144 | + const { specs } = dataType || {} | ||
145 | + const { boolClose, boolOpen } = specs || {} | ||
146 | + this.boolList = [ | ||
147 | + { label: boolClose + '0', value: 0 }, | ||
148 | + { label: boolOpen + '0', value: 1 }, | ||
149 | + ] | ||
150 | + this.isShowBool = true | ||
151 | + }, | ||
152 | + handleSelectBool(e, name, num) { | ||
153 | + this.isShowBool = false | ||
154 | + const { value } = e || {} | ||
155 | + this.boolInfo[name] = value[0].value | ||
156 | + this.$set(this.seriesForm, name, value[0].label) | ||
157 | + this.seriesFunctionList.forEach((item, index) => { | ||
158 | + if (index == num) { | ||
159 | + item.isShowModel = false | ||
160 | + this.showPickerView=false | ||
161 | + } | ||
162 | + }) | ||
163 | + }, | ||
164 | + | ||
165 | + getFormField() { | ||
166 | + return { | ||
167 | + ...this.seriesForm, | ||
168 | + ...this.boolInfo, | ||
169 | + ...this.enumInfo, | ||
170 | + } | ||
171 | + }, | ||
172 | + | ||
173 | + handleEnum(name, dataType, num) { | ||
174 | + const { specsList } = dataType || {} | ||
175 | + this.isShowEnum = true | ||
176 | + this.enumList = specsList || [] | ||
177 | + if(!this.enumList.length){ | ||
178 | + return uni.$u.toast(`暂无可选的${name}`) | ||
179 | + } | ||
180 | + this.seriesFunctionList.forEach((item, index) => { | ||
181 | + if (index == num) { | ||
182 | + item.isShowModel = true | ||
183 | + this.showPickerView=true | ||
184 | + } | ||
185 | + }) | ||
186 | + }, | ||
187 | + handleSelectEnum(e, name, num) { | ||
188 | + const { value } = e || {} | ||
189 | + this.enumInfo[name] = value[0].value | ||
190 | + this.$set(this.seriesForm, name, value[0].label) | ||
191 | + this.seriesFunctionList.forEach((item, index) => { | ||
192 | + if (index == num) { | ||
193 | + item.isShowModel = false | ||
194 | + this.showPickerView=false | ||
195 | + } | ||
196 | + }) | ||
197 | + }, | ||
198 | + handleCancel(num){ | ||
199 | + this.seriesFunctionList.forEach((item, index) => { | ||
200 | + if (index == num) { | ||
201 | + item.isShowModel = false | ||
202 | + this.showPickerView=false | ||
203 | + } | ||
204 | + }) | ||
205 | + }, | ||
206 | + | ||
207 | + handleValidate() { | ||
208 | + return this.$refs.seriesRef?.validate((valid) => { | ||
209 | + if (!valid) return | ||
210 | + }) | ||
211 | + }, | ||
212 | + }, | ||
213 | +} | ||
214 | +</script> | ||
215 | + | ||
216 | +<style lang="scss" scoped></style> |
@@ -35,15 +35,12 @@ | @@ -35,15 +35,12 @@ | ||
35 | <u-form-item labelWidth="80px" label="用户账号" prop="username" borderBottom> | 35 | <u-form-item labelWidth="80px" label="用户账号" prop="username" borderBottom> |
36 | <u--input disabled placeholder="请输入用户账号 " v-model="myInfoModel.username" border="none"></u--input> | 36 | <u--input disabled placeholder="请输入用户账号 " v-model="myInfoModel.username" border="none"></u--input> |
37 | </u-form-item> | 37 | </u-form-item> |
38 | + <u-form-item labelWidth="80px" label="有效期" prop="accountExpireTime" borderBottom> | ||
39 | + <u--input disabled v-model="myInfoModel.accountExpireTime" border="none"></u--input> | ||
40 | + </u-form-item> | ||
38 | <u-form-item labelWidth="80px" label="邮箱地址" prop="email" borderBottom> | 41 | <u-form-item labelWidth="80px" label="邮箱地址" prop="email" borderBottom> |
39 | <u--input placeholder="请输入邮箱地址" v-model="myInfoModel.email" border="none"></u--input> | 42 | <u--input placeholder="请输入邮箱地址" v-model="myInfoModel.email" border="none"></u--input> |
40 | </u-form-item> | 43 | </u-form-item> |
41 | - <u-form-item @click="hideKeyboard" labelWidth="80px" label="有效期" prop="accountExpireTime"> | ||
42 | - <u--input v-model="myInfoModel.accountExpireTime" placeholder="请选择有效期" border="none"></u--input> | ||
43 | - <u-datetime-picker :formatter="formatter" :show="showDate" :value="datetime" mode="dateTime" | ||
44 | - closeOnClickOverlay @confirm="dateConfirm" @cancel="dateClose" @close="dateClose"> | ||
45 | - </u-datetime-picker> | ||
46 | - </u-form-item> | ||
47 | </u--form> | 44 | </u--form> |
48 | </view> | 45 | </view> |
49 | <view class="basic-bottom u-flex"> | 46 | <view class="basic-bottom u-flex"> |
@@ -403,4 +400,10 @@ | @@ -403,4 +400,10 @@ | ||
403 | width: 663rpx !important; | 400 | width: 663rpx !important; |
404 | } | 401 | } |
405 | } | 402 | } |
403 | + /deep/ .u-form-item:nth-child(4) { | ||
404 | + .u-form-item__body { | ||
405 | + background: #f5f7fa !important; | ||
406 | + width: 663rpx !important; | ||
407 | + } | ||
408 | + } | ||
406 | </style> | 409 | </style> |
@@ -69,15 +69,17 @@ | @@ -69,15 +69,17 @@ | ||
69 | this.conditions = { | 69 | this.conditions = { |
70 | deviceState: params | 70 | deviceState: params |
71 | } | 71 | } |
72 | - this.loadData(1, { | ||
73 | - deviceState: params | ||
74 | - }); | 72 | + // this.loadData(1, { |
73 | + // deviceState: params | ||
74 | + // }); | ||
75 | } | 75 | } |
76 | }, | 76 | }, |
77 | onShow() { | 77 | onShow() { |
78 | + console.log(2) | ||
78 | if (getApp().getBindNot()) { | 79 | if (getApp().getBindNot()) { |
79 | return | 80 | return |
80 | } | 81 | } |
82 | + this.page.num=1 | ||
81 | if (this.ordId) { | 83 | if (this.ordId) { |
82 | this.loadData(1, { | 84 | this.loadData(1, { |
83 | organizationId: this.ordId | 85 | organizationId: this.ordId |
@@ -87,7 +89,7 @@ | @@ -87,7 +89,7 @@ | ||
87 | } | 89 | } |
88 | return | 90 | return |
89 | } | 91 | } |
90 | - this.loadData(1); | 92 | + this.loadData(this.page.num,this.conditions); |
91 | }, | 93 | }, |
92 | methods: { | 94 | methods: { |
93 | inputChanged(e) { | 95 | inputChanged(e) { |
uni_modules/tn-popup/changelog.md
0 → 100644
uni_modules/tn-popup/color.js
0 → 100644
1 | +let color = [ | ||
2 | + 'red', | ||
3 | + 'purplered', | ||
4 | + 'purple', | ||
5 | + 'bluepurple', | ||
6 | + 'aquablue', | ||
7 | + 'blue', | ||
8 | + 'indigo', | ||
9 | + 'cyan', | ||
10 | + 'teal', | ||
11 | + 'green', | ||
12 | + 'yellowgreen', | ||
13 | + 'lime', | ||
14 | + 'yellow', | ||
15 | + 'orangeyellow', | ||
16 | + 'orange', | ||
17 | + 'orangered', | ||
18 | + 'brown', | ||
19 | + 'grey', | ||
20 | + 'gray' | ||
21 | +] | ||
22 | + | ||
23 | +// 酷炫颜色的数量 | ||
24 | +const COOL_BG_COLOR_COUNT = 16 | ||
25 | + | ||
26 | +/** | ||
27 | + * 获取图鸟配色颜色列表 | ||
28 | + */ | ||
29 | +function getTuniaoColorList() { | ||
30 | + return color | ||
31 | +} | ||
32 | + | ||
33 | +/** | ||
34 | + * 获取指定类型的随机颜色对应的类 | ||
35 | + * @param {String} type 颜色类型 | ||
36 | + */ | ||
37 | +function getRandomColorClass(type = 'bg') { | ||
38 | + const index = Math.floor(Math.random() * color.length) | ||
39 | + const colorValue = color[index] | ||
40 | + | ||
41 | + return 'tn-' + type + '-' + colorValue | ||
42 | +} | ||
43 | + | ||
44 | +/** | ||
45 | + * 随机获取酷炫背景对应的类 | ||
46 | + */ | ||
47 | +function getRandomCoolBgClass() { | ||
48 | + const index = (Math.random() * COOL_BG_COLOR_COUNT) + 1 | ||
49 | + return 'tn-cool-bg-color-' + Math.floor(index) | ||
50 | +} | ||
51 | + | ||
52 | +/** | ||
53 | + * 根据传入的值获取内部背景颜色类 | ||
54 | + * | ||
55 | + * @param {String} backgroundColor 背景颜色信息 | ||
56 | + */ | ||
57 | +function getBackgroundColorInternalClass(backgroundColor = '') { | ||
58 | + if (!backgroundColor) return '' | ||
59 | + | ||
60 | + if (['tn-bg', 'tn-dynamic-bg', 'tn-main-gradient', 'tn-cool-bg'].some(item => { | ||
61 | + return backgroundColor.includes(item) | ||
62 | + })) { | ||
63 | + return backgroundColor | ||
64 | + } | ||
65 | + return '' | ||
66 | +} | ||
67 | + | ||
68 | +/** | ||
69 | + * 根据传入的值获取背景颜色样式 | ||
70 | + * | ||
71 | + * @param {String} backgroundColor 背景颜色信息 | ||
72 | + */ | ||
73 | +function getBackgroundColorStyle(backgroundColor = '') { | ||
74 | + if (!backgroundColor) return '' | ||
75 | + | ||
76 | + if (!backgroundColor.startsWith('tn-') || ['#', 'rgb', 'rgba'].some(item => { | ||
77 | + return backgroundColor.includes(item) | ||
78 | + })) { | ||
79 | + return backgroundColor | ||
80 | + } | ||
81 | + return '' | ||
82 | +} | ||
83 | + | ||
84 | +/** | ||
85 | + * 根据传入的值获取内部字体颜色类 | ||
86 | + * | ||
87 | + * @param {String} fontColor 背景颜色信息 | ||
88 | + */ | ||
89 | +function getFontColorInternalClass(fontColor = '') { | ||
90 | + if (!fontColor) return '' | ||
91 | + | ||
92 | + if (['tn-color'].some(item => { | ||
93 | + return fontColor.includes(item) | ||
94 | + })) { | ||
95 | + return fontColor | ||
96 | + } | ||
97 | + return '' | ||
98 | +} | ||
99 | + | ||
100 | +/** | ||
101 | + * 根据传入的值获取字体颜色样式 | ||
102 | + * | ||
103 | + * @param {String} fontColor 背景颜色信息 | ||
104 | + */ | ||
105 | +function getFontColorStyle(fontColor = '') { | ||
106 | + if (!fontColor) return '' | ||
107 | + | ||
108 | + if (!fontColor.startsWith('tn-') || ['#', 'rgb', 'rgba'].some(item => { | ||
109 | + return fontColor.includes(item) | ||
110 | + })) { | ||
111 | + return fontColor | ||
112 | + } | ||
113 | + return '' | ||
114 | +} | ||
115 | + | ||
116 | +/** | ||
117 | + * 求两个颜色之间的渐变值 | ||
118 | + * | ||
119 | + * @param {String} startColor 开始颜色 | ||
120 | + * @param {String} endColor 结束颜色 | ||
121 | + * @param {Number} step 颜色等分的份额 | ||
122 | + */ | ||
123 | +function colorGradient(startColor = 'rgb(0, 0, 0)', endColor='rgb(255, 255, 255)', step = 10) { | ||
124 | + let startRGB = hexToRGB(startColor, false) | ||
125 | + let startR = startRGB[0] | ||
126 | + let startG = startRGB[1] | ||
127 | + let startB = startRGB[2] | ||
128 | + | ||
129 | + let endRGB = hexToRGB(endColor, false) | ||
130 | + let endR = endRGB[0] | ||
131 | + let endG = endRGB[1] | ||
132 | + let endB = endRGB[2] | ||
133 | + | ||
134 | + // 求差值 | ||
135 | + let R = (endR - startR) / step | ||
136 | + let G = (endG - startG) / step | ||
137 | + let B = (endB - startB) / step | ||
138 | + | ||
139 | + let colorArr = [] | ||
140 | + for (let i = 0; i < step; i++) { | ||
141 | + // 计算每一步的hex值 | ||
142 | + let hex = rgbToHex(`rgb(${Math.round(R * i + startR)}, ${Math.round(G * i + startG)}, ${Math.round(B * i + startB)})`) | ||
143 | + colorArr.push(hex) | ||
144 | + } | ||
145 | + return colorArr | ||
146 | +} | ||
147 | + | ||
148 | +/** | ||
149 | + * 将hex的颜色表示方式转换为rgb表示方式 | ||
150 | + * | ||
151 | + * @param {String} color 颜色 | ||
152 | + * @param {Boolean} str 是否返回字符串 | ||
153 | + * @return {Array|String} rgb的值 | ||
154 | + */ | ||
155 | +function hexToRGB(color, str = true) { | ||
156 | + let reg = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/ | ||
157 | + | ||
158 | + color = color.toLowerCase() | ||
159 | + if (color && reg.test(color)) { | ||
160 | + // #000 => #000000 | ||
161 | + if (color.length === 4) { | ||
162 | + let colorNew = '#' | ||
163 | + for (let i = 1; i < 4; i++) { | ||
164 | + colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1)) | ||
165 | + } | ||
166 | + color = colorNew | ||
167 | + } | ||
168 | + // 处理六位的颜色值 | ||
169 | + let colorChange = [] | ||
170 | + for (let i = 1; i < 7; i += 2) { | ||
171 | + colorChange.push(parseInt("0x" + color.slice(i, i + 2))) | ||
172 | + } | ||
173 | + if (!str) { | ||
174 | + return colorChange | ||
175 | + } else { | ||
176 | + return `rgb(${colorChange[0]}, ${colorChange[1]}, ${colorChange[2]})` | ||
177 | + } | ||
178 | + } else if (/^(rgb|RGB)/.test(color)) { | ||
179 | + let arr = color.replace(/(?:\(|\)|rgb|RGB)*/g, "").split(',') | ||
180 | + return arr.map(item => Number(item)) | ||
181 | + } else { | ||
182 | + return color | ||
183 | + } | ||
184 | +} | ||
185 | + | ||
186 | +/** | ||
187 | + * 将rgb的颜色表示方式转换成hex表示方式 | ||
188 | + * | ||
189 | + * @param {Object} rgb rgb颜色值 | ||
190 | + */ | ||
191 | +function rgbToHex(rgb) { | ||
192 | + let reg = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/ | ||
193 | + if (/^(rgb|RGB)/.test(rgb)) { | ||
194 | + let color = rgb.replace(/(?:\(|\)|rgb|GRB)*/g, "").split(',') | ||
195 | + let strHex = '#' | ||
196 | + for (let i = 0; i < color.length; i++) { | ||
197 | + let hex = Number(color[i]).toString(16) | ||
198 | + // 保证每个值否是两位数 | ||
199 | + hex = String(hex).length === 1 ? 0 + '' + hex: hex | ||
200 | + if (hex === '0') { | ||
201 | + hex += hex | ||
202 | + } | ||
203 | + strHex += hex | ||
204 | + } | ||
205 | + if (strHex.length !== 7) { | ||
206 | + strHex = rgb | ||
207 | + } | ||
208 | + return strHex | ||
209 | + } else if (reg.test(rgb)) { | ||
210 | + let num = rgb.replace(/#/, '').split('') | ||
211 | + if (num.length === 6) { | ||
212 | + return rgb | ||
213 | + } else if (num.length === 3) { | ||
214 | + let numHex = '#' | ||
215 | + for (let i = 0; i < num.length; i++) { | ||
216 | + numHex += (num[i] + num[i]) | ||
217 | + } | ||
218 | + return numHex | ||
219 | + } | ||
220 | + } else { | ||
221 | + return rgb | ||
222 | + } | ||
223 | +} | ||
224 | + | ||
225 | +/** | ||
226 | + * 将传入的颜色值转换为rgba字符串 | ||
227 | + * | ||
228 | + * @param {String} color 颜色 | ||
229 | + * @param {Number} alpha 透明度 | ||
230 | + */ | ||
231 | +function colorToRGBA(color, alpha = 0.3) { | ||
232 | + color = rgbToHex(color) | ||
233 | + // 十六进制颜色值的正则表达式 | ||
234 | + let reg = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/ | ||
235 | + | ||
236 | + color = color.toLowerCase() | ||
237 | + if (color && reg.test(color)) { | ||
238 | + // #000 => #000000 | ||
239 | + if (color.length === 4) { | ||
240 | + let colorNew = '#' | ||
241 | + for (let i = 1; i < 4; i++) { | ||
242 | + colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1)) | ||
243 | + } | ||
244 | + color = colorNew | ||
245 | + } | ||
246 | + // 处理六位的颜色值 | ||
247 | + let colorChange = [] | ||
248 | + for (let i = 1; i < 7; i += 2) { | ||
249 | + colorChange.push(parseInt("0x" + color.slice(i, i + 2))) | ||
250 | + } | ||
251 | + return `rgba(${colorChange[0]}, ${colorChange[1]}, ${colorChange[2]}, ${alpha})` | ||
252 | + } else { | ||
253 | + return color | ||
254 | + } | ||
255 | +} | ||
256 | + | ||
257 | +export default { | ||
258 | + COOL_BG_COLOR_COUNT: COOL_BG_COLOR_COUNT, | ||
259 | + getTuniaoColorList, | ||
260 | + getRandomColorClass, | ||
261 | + getRandomCoolBgClass, | ||
262 | + getBackgroundColorInternalClass, | ||
263 | + getBackgroundColorStyle, | ||
264 | + getFontColorInternalClass, | ||
265 | + getFontColorStyle, | ||
266 | + colorGradient, | ||
267 | + hexToRGB, | ||
268 | + rgbToHex, | ||
269 | + colorToRGBA | ||
270 | +} |
1 | +<template> | ||
2 | + <view | ||
3 | + v-if="visibleSync" | ||
4 | + class="tn-popup-class tn-popup" | ||
5 | + :style="[customStyle, popupStyle, { zIndex: elZIndex - 1}]" | ||
6 | + hover-stop-propagation | ||
7 | + > | ||
8 | + <!-- mask --> | ||
9 | + <view | ||
10 | + class="tn-popup__mask" | ||
11 | + :class="[{'tn-popup__mask--show': showPopup && mask}]" | ||
12 | + :style="{zIndex: elZIndex - 2}" | ||
13 | + @tap="maskClick" | ||
14 | + @touchmove.stop.prevent = "() => {}" | ||
15 | + hover-stop-propagation | ||
16 | + ></view> | ||
17 | + <!-- 弹框内容 --> | ||
18 | + <view | ||
19 | + class="tn-popup__content" | ||
20 | + :class="[ | ||
21 | + mode !== 'center' ? backgroundColorClass : '', | ||
22 | + safeAreaInsetBottom ? 'tn-safe-area-inset-bottom' : '', | ||
23 | + 'tn-popup--' + mode, | ||
24 | + showPopup ? 'tn-popup__content--visible' : '', | ||
25 | + zoom && mode === 'center' ? 'tn-popup__content__center--animation-zoom' : '' | ||
26 | + ]" | ||
27 | + :style="[contentStyle]" | ||
28 | + @tap="modeCenterClose" | ||
29 | + @touchmove.stop.prevent | ||
30 | + @tap.stop.prevent | ||
31 | + > | ||
32 | + <!-- 居中时候的内容 --> | ||
33 | + <view | ||
34 | + v-if="mode === 'center'" | ||
35 | + class="tn-popup__content__center_box" | ||
36 | + :class="[backgroundColorClass]" | ||
37 | + :style="[centerStyle]" | ||
38 | + @touchmove.stop.prevent | ||
39 | + @tap.stop.prevent | ||
40 | + > | ||
41 | + <!-- 关闭按钮 --> | ||
42 | + <view | ||
43 | + v-if="closeBtn" | ||
44 | + class="tn-popup__close" | ||
45 | + :class="[`tn-icon-${closeBtnIcon}`, `tn-popup__close--${closeBtnPosition}`]" | ||
46 | + :style="[closeBtnStyle, {zIndex: elZIndex}]" | ||
47 | + @tap="close" | ||
48 | + ></view> | ||
49 | + <scroll-view scroll-y class="tn-popup__content__scroll-view"> | ||
50 | + <slot></slot> | ||
51 | + </scroll-view> | ||
52 | + </view> | ||
53 | + | ||
54 | + <!-- 除居中外的其他情况 --> | ||
55 | + <scroll-view scroll-y v-else class="tn-popup__content__scroll-view"> | ||
56 | + <slot></slot> | ||
57 | + </scroll-view> | ||
58 | + <!-- 关闭按钮 --> | ||
59 | + <view | ||
60 | + v-if="mode !== 'center' && closeBtn" | ||
61 | + class="tn-popup__close" | ||
62 | + :class="[`tn-popup__close--${closeBtnPosition}`]" | ||
63 | + :style="{zIndex: elZIndex}" | ||
64 | + @tap="close" | ||
65 | + > | ||
66 | + <view :class="[`tn-icon-${closeBtnIcon}`]" :style="[closeBtnStyle]"></view> | ||
67 | + </view> | ||
68 | + </view> | ||
69 | + </view> | ||
70 | +</template> | ||
71 | + | ||
72 | +<script> | ||
73 | + import componentsColorMixin from '../../components_color.js' | ||
74 | + // 格式化字符串工具 | ||
75 | + import string from '../../string.js' | ||
76 | + export default { | ||
77 | + mixins: [componentsColorMixin], | ||
78 | + name: 'tn-popup', | ||
79 | + props: { | ||
80 | + value: { | ||
81 | + type: Boolean, | ||
82 | + default: false | ||
83 | + }, | ||
84 | + // 弹出方向 | ||
85 | + // left/right/top/bottom/center | ||
86 | + mode: { | ||
87 | + type: String, | ||
88 | + default: 'left' | ||
89 | + }, | ||
90 | + // 是否显示遮罩 | ||
91 | + mask: { | ||
92 | + type: Boolean, | ||
93 | + default: true | ||
94 | + }, | ||
95 | + // 抽屉的宽度(mode=left/right),高度(mode=top/bottom) | ||
96 | + length: { | ||
97 | + type: [Number, String], | ||
98 | + default: 'auto' | ||
99 | + }, | ||
100 | + // 宽度,只对左,右,中部弹出时起作用,单位rpx,或者"auto" | ||
101 | + // 或者百分比"50%",表示由内容撑开高度或者宽度,优先级高于length参数 | ||
102 | + width: { | ||
103 | + type: String, | ||
104 | + default: '' | ||
105 | + }, | ||
106 | + // 高度,只对上,下,中部弹出时起作用,单位rpx,或者"auto" | ||
107 | + // 或者百分比"50%",表示由内容撑开高度或者宽度,优先级高于length参数 | ||
108 | + height: { | ||
109 | + type: String, | ||
110 | + default: '' | ||
111 | + }, | ||
112 | + // 是否开启动画,只在mode=center有效 | ||
113 | + zoom: { | ||
114 | + type: Boolean, | ||
115 | + default: true | ||
116 | + }, | ||
117 | + // 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距 | ||
118 | + safeAreaInsetBottom: { | ||
119 | + type: Boolean, | ||
120 | + default: false | ||
121 | + }, | ||
122 | + // 是否可以通过点击遮罩进行关闭 | ||
123 | + maskCloseable: { | ||
124 | + type: Boolean, | ||
125 | + default: true | ||
126 | + }, | ||
127 | + // 用户自定义样式 | ||
128 | + customStyle: { | ||
129 | + type: Object, | ||
130 | + default() { | ||
131 | + return {} | ||
132 | + } | ||
133 | + }, | ||
134 | + // 显示圆角的大小 | ||
135 | + borderRadius: { | ||
136 | + type: Number, | ||
137 | + default: 0 | ||
138 | + }, | ||
139 | + // zIndex | ||
140 | + zIndex: { | ||
141 | + type: Number, | ||
142 | + default: 0 | ||
143 | + }, | ||
144 | + // 是否显示关闭按钮 | ||
145 | + closeBtn: { | ||
146 | + type: Boolean, | ||
147 | + default: false | ||
148 | + }, | ||
149 | + // 关闭按钮的图标 | ||
150 | + closeBtnIcon: { | ||
151 | + type: String, | ||
152 | + default: 'close' | ||
153 | + }, | ||
154 | + // 关闭按钮显示的位置 | ||
155 | + // top-left/top-right/bottom-left/bottom-right | ||
156 | + closeBtnPosition: { | ||
157 | + type: String, | ||
158 | + default: 'top-right' | ||
159 | + }, | ||
160 | + // 关闭按钮图标颜色 | ||
161 | + closeIconColor: { | ||
162 | + type: String, | ||
163 | + default: '#AAAAAA' | ||
164 | + }, | ||
165 | + // 关闭按钮图标的大小 | ||
166 | + closeIconSize: { | ||
167 | + type: Number, | ||
168 | + default: 30 | ||
169 | + }, | ||
170 | + // 给一个负的margin-top,往上偏移,避免和键盘重合的情况,仅在mode=center时有效 | ||
171 | + negativeTop: { | ||
172 | + type: Number, | ||
173 | + default: 0 | ||
174 | + }, | ||
175 | + // marginTop,在mode = top,left,right时生效,避免用户使用了自定义导航栏,组件把导航栏遮挡了 | ||
176 | + marginTop: { | ||
177 | + type: Number, | ||
178 | + default: 0 | ||
179 | + }, | ||
180 | + // 此为内部参数,不在文档对外使用,为了解决Picker和keyboard等融合了弹窗的组件 | ||
181 | + // 对v-model双向绑定多层调用造成报错不能修改props值的问题 | ||
182 | + popup: { | ||
183 | + type: Boolean, | ||
184 | + default: true | ||
185 | + }, | ||
186 | + }, | ||
187 | + computed: { | ||
188 | + // 处理使用了自定义导航栏时被遮挡的问题 | ||
189 | + popupStyle() { | ||
190 | + let style = {} | ||
191 | + if ((this.mode === 'top' || this.mode === 'left' || this.mode === 'right') && this.marginTop) { | ||
192 | + style.marginTop = string.getLengthUnitValue(this.marginTop, 'px') | ||
193 | + } | ||
194 | + | ||
195 | + return style | ||
196 | + }, | ||
197 | + // 根据mode的位置,设定其弹窗的宽度(mode = left|right),或者高度(mode = top|bottom) | ||
198 | + contentStyle() { | ||
199 | + let style = {} | ||
200 | + // 如果是左边或者上边弹出时,需要给translate设置为负值,用于隐藏 | ||
201 | + if (this.mode === 'left' || this.mode === 'right') { | ||
202 | + style = { | ||
203 | + width: this.width ? string.getLengthUnitValue(this.width) : string.getLengthUnitValue(this.length), | ||
204 | + height: '100%', | ||
205 | + transform: `translate3D(${this.mode === 'left' ? '-100%' : '100%'}, 0px, 0px)` | ||
206 | + } | ||
207 | + } else if (this.mode === 'top' || this.mode === 'bottom') { | ||
208 | + style = { | ||
209 | + width: '100%', | ||
210 | + height: this.height ? string.getLengthUnitValue(this.height) : string.getLengthUnitValue(this.length), | ||
211 | + transform: `translate3D(0px, ${this.mode === 'top' ? '-100%': '100%'}, 0px)` | ||
212 | + } | ||
213 | + } | ||
214 | + style.zIndex = this.elZIndex | ||
215 | + // 如果设置了圆角的值,添加弹窗的圆角 | ||
216 | + if (this.borderRadius) { | ||
217 | + switch(this.mode) { | ||
218 | + case 'left': | ||
219 | + style.borderRadius = `0 ${this.borderRadius}rpx ${this.borderRadius}rpx 0` | ||
220 | + break | ||
221 | + case 'top': | ||
222 | + style.borderRadius = `0 0 ${this.borderRadius}rpx ${this.borderRadius}rpx` | ||
223 | + break | ||
224 | + case 'right': | ||
225 | + style.borderRadius = `${this.borderRadius}rpx 0 0 ${this.borderRadius}rpx` | ||
226 | + break | ||
227 | + case 'bottom': | ||
228 | + style.borderRadius = `${this.borderRadius}rpx ${this.borderRadius}rpx 0 0` | ||
229 | + break | ||
230 | + } | ||
231 | + style.overflow = 'hidden' | ||
232 | + } | ||
233 | + | ||
234 | + if (this.backgroundColorStyle && this.mode !== 'center') { | ||
235 | + style.backgroundColor = this.backgroundColorStyle | ||
236 | + } | ||
237 | + | ||
238 | + return style | ||
239 | + }, | ||
240 | + // 中部弹窗的样式 | ||
241 | + centerStyle() { | ||
242 | + let style = {} | ||
243 | + style.width = this.width ? string.getLengthUnitValue(this.width) : string.getLengthUnitValue(this.length) | ||
244 | + // 中部弹出的模式,如果没有设置高度,就用auto值,由内容撑开 | ||
245 | + style.height = this.height ? string.getLengthUnitValue(this.height) : 'auto' | ||
246 | + style.zIndex = this.elZIndex | ||
247 | + if (this.negativeTop) { | ||
248 | + style.marginTop = `-${string.getLengthUnitValue(this.negativeTop)}` | ||
249 | + } | ||
250 | + if (this.borderRadius) { | ||
251 | + style.borderRadius = `${this.borderRadius}rpx` | ||
252 | + style.overflow='hidden' | ||
253 | + } | ||
254 | + if (this.backgroundColorStyle) { | ||
255 | + style.backgroundColor = this.backgroundColorStyle | ||
256 | + } | ||
257 | + return style | ||
258 | + }, | ||
259 | + // 关闭按钮样式 | ||
260 | + closeBtnStyle() { | ||
261 | + let style = {} | ||
262 | + if (this.closeIconColor) { | ||
263 | + style.color = this.closeIconColor | ||
264 | + } | ||
265 | + if (this.closeIconSize) { | ||
266 | + style.fontSize = this.closeIconSize + 'rpx' | ||
267 | + } | ||
268 | + | ||
269 | + return style | ||
270 | + }, | ||
271 | + elZIndex() { | ||
272 | + return this.zIndex ? this.zIndex : 20075 | ||
273 | + } | ||
274 | + }, | ||
275 | + data() { | ||
276 | + return { | ||
277 | + timer: null, | ||
278 | + visibleSync: false, | ||
279 | + showPopup: false, | ||
280 | + closeFromInner: false | ||
281 | + } | ||
282 | + }, | ||
283 | + watch: { | ||
284 | + value(val) { | ||
285 | + if (val) { | ||
286 | + // console.log(this.visibleSync); | ||
287 | + if (this.visibleSync) { | ||
288 | + this.visibleSync = false | ||
289 | + return | ||
290 | + } | ||
291 | + this.open() | ||
292 | + } else if (!this.closeFromInner) { | ||
293 | + this.close() | ||
294 | + } | ||
295 | + this.closeFromInner = false | ||
296 | + } | ||
297 | + }, | ||
298 | + mounted() { | ||
299 | + // 组件渲染完成时,检查value是否为true,如果是,弹出popup | ||
300 | + this.value && this.open() | ||
301 | + }, | ||
302 | + methods: { | ||
303 | + // 点击遮罩 | ||
304 | + maskClick() { | ||
305 | + if (!this.maskCloseable) return | ||
306 | + this.close() | ||
307 | + }, | ||
308 | + open() { | ||
309 | + this.change('visibleSync', 'showPopup', true) | ||
310 | + }, | ||
311 | + // 关闭弹框 | ||
312 | + close() { | ||
313 | + // 标记关闭是内部发生的,否则修改了value值,导致watch中对value检测,导致再执行一遍close | ||
314 | + // 造成@close事件触发两次 | ||
315 | + this.closeFromInner = true | ||
316 | + this.change('showPopup', 'visibleSync', false) | ||
317 | + }, | ||
318 | + // 中部弹出时,需要.tn-drawer-content将内容居中,此元素会铺满屏幕,点击需要关闭弹窗 | ||
319 | + // 让其只在mode=center时起作用 | ||
320 | + modeCenterClose() { | ||
321 | + if (this.mode != 'center' || !this.maskCloseable) return | ||
322 | + this.close() | ||
323 | + }, | ||
324 | + // 关闭时先通过动画隐藏弹窗和遮罩,再移除整个组件 | ||
325 | + // 打开时,先渲染组件,延时一定时间再让遮罩和弹窗的动画起作用 | ||
326 | + change(param1, param2, status) { | ||
327 | + // 如果this.popup为false,意味着为picker,actionsheet等组件调用了popup组件 | ||
328 | + if (this.popup === true) { | ||
329 | + this.$emit('input', status) | ||
330 | + } | ||
331 | + this[param1] = status | ||
332 | + if (status) { | ||
333 | + // #ifdef H5 || MP | ||
334 | + this.timer = setTimeout(() => { | ||
335 | + this[param2] = status | ||
336 | + this.$emit(status ? 'open' : 'close') | ||
337 | + clearTimeout(this.timer) | ||
338 | + }, 10) | ||
339 | + // #endif | ||
340 | + // #ifndef H5 || MP | ||
341 | + this.$nextTick(() => { | ||
342 | + this[param2] = status | ||
343 | + this.$emit(status ? 'open' : 'close') | ||
344 | + }) | ||
345 | + // #endif | ||
346 | + } else { | ||
347 | + this.timer = setTimeout(() => { | ||
348 | + this[param2] = status | ||
349 | + this.$emit(status ? 'open' : 'close') | ||
350 | + clearTimeout(this.timer) | ||
351 | + }, 250) | ||
352 | + } | ||
353 | + } | ||
354 | + } | ||
355 | + } | ||
356 | +</script> | ||
357 | + | ||
358 | +<style lang="scss" scoped> | ||
359 | + @import '../../theme.scss'; | ||
360 | + .tn-popup { | ||
361 | + /* #ifndef APP-NVUE */ | ||
362 | + display: block; | ||
363 | + /* #endif */ | ||
364 | + position: fixed; | ||
365 | + top: 0; | ||
366 | + left: 0; | ||
367 | + right: 0; | ||
368 | + bottom: 0; | ||
369 | + overflow: hidden; | ||
370 | + z-index: 29091 !important; | ||
371 | + | ||
372 | + &__content { | ||
373 | + /* #ifndef APP-NVUE */ | ||
374 | + display: block; | ||
375 | + /* #endif */ | ||
376 | + position: absolute; | ||
377 | + transition: all 0.25s linear; | ||
378 | + | ||
379 | + &--visible { | ||
380 | + transform: translate3D(0px, 0px, 0px) !important; | ||
381 | + &.tn-popup--center { | ||
382 | + transform: scale(1); | ||
383 | + opacity: 1; | ||
384 | + } | ||
385 | + } | ||
386 | + | ||
387 | + &__center_box { | ||
388 | + min-width: 100rpx; | ||
389 | + min-height: 100rpx; | ||
390 | + /* #ifndef APP-NVUE */ | ||
391 | + display: block; | ||
392 | + /* #endif */ | ||
393 | + position: relative; | ||
394 | + background-color: #FFFFFF; | ||
395 | + } | ||
396 | + | ||
397 | + &__scroll-view { | ||
398 | + width: 100%; | ||
399 | + height: 100%; | ||
400 | + } | ||
401 | + | ||
402 | + &__center--animation-zoom { | ||
403 | + transform: scale(1.15); | ||
404 | + } | ||
405 | + } | ||
406 | + | ||
407 | + &__scroll_view { | ||
408 | + width: 100%; | ||
409 | + height: 100%; | ||
410 | + } | ||
411 | + | ||
412 | + &--left { | ||
413 | + top: 0; | ||
414 | + bottom: 0; | ||
415 | + left: 0; | ||
416 | + background-color: #FFFFFF; | ||
417 | + } | ||
418 | + | ||
419 | + &--right { | ||
420 | + top: 0; | ||
421 | + bottom: 0; | ||
422 | + right: 0; | ||
423 | + background-color: #FFFFFF; | ||
424 | + } | ||
425 | + | ||
426 | + &--top { | ||
427 | + left: 0; | ||
428 | + right: 0; | ||
429 | + top: 0; | ||
430 | + background-color: #FFFFFF; | ||
431 | + } | ||
432 | + | ||
433 | + &--bottom { | ||
434 | + left: 0; | ||
435 | + right: 0; | ||
436 | + bottom: 0; | ||
437 | + background-color: #FFFFFF; | ||
438 | + } | ||
439 | + | ||
440 | + &--center { | ||
441 | + display: flex; | ||
442 | + flex-direction: column; | ||
443 | + bottom: 0; | ||
444 | + top: 0; | ||
445 | + left: 0; | ||
446 | + right: 0; | ||
447 | + justify-content: center; | ||
448 | + align-items: center; | ||
449 | + opacity: 0; | ||
450 | + } | ||
451 | + | ||
452 | + &__close { | ||
453 | + position: absolute; | ||
454 | + | ||
455 | + &--top-left { | ||
456 | + top: 30rpx; | ||
457 | + left: 30rpx; | ||
458 | + } | ||
459 | + | ||
460 | + &--top-right { | ||
461 | + top: 30rpx; | ||
462 | + right: 30rpx; | ||
463 | + } | ||
464 | + | ||
465 | + &--bottom-left { | ||
466 | + bottom: 30rpx; | ||
467 | + left: 30rpx; | ||
468 | + } | ||
469 | + | ||
470 | + &--bottom-right { | ||
471 | + bottom: 30rpx; | ||
472 | + right: 30rpx; | ||
473 | + } | ||
474 | + } | ||
475 | + | ||
476 | + &__mask { | ||
477 | + width: 100%; | ||
478 | + height: 100%; | ||
479 | + position: fixed; | ||
480 | + top: 0; | ||
481 | + left: 0; | ||
482 | + right: 0; | ||
483 | + border: 0; | ||
484 | + background-color: $tn-mask-bg-color; | ||
485 | + transition: 0.25s linear; | ||
486 | + transition-property: opacity; | ||
487 | + opacity: 0; | ||
488 | + | ||
489 | + &--show { | ||
490 | + opacity: 1; | ||
491 | + } | ||
492 | + } | ||
493 | + } | ||
494 | +</style> |
uni_modules/tn-popup/components_color.js
0 → 100644
1 | +// 格式化字符串工具 | ||
2 | +import string from './string.js' | ||
3 | +// 获取颜色工具 | ||
4 | +import color from './color.js' | ||
5 | +module.exports = { | ||
6 | + data() { | ||
7 | + | ||
8 | + }, | ||
9 | + props: { | ||
10 | + // 背景颜色 | ||
11 | + backgroundColor: { | ||
12 | + type: String, | ||
13 | + default: '' | ||
14 | + }, | ||
15 | + // 字体颜色 | ||
16 | + fontColor: { | ||
17 | + type: String, | ||
18 | + default: '' | ||
19 | + }, | ||
20 | + // 字体大小 | ||
21 | + fontSize: { | ||
22 | + type: Number, | ||
23 | + default: 0 | ||
24 | + }, | ||
25 | + // 字体大小单位 | ||
26 | + fontUnit: { | ||
27 | + type: String, | ||
28 | + default: 'rpx' | ||
29 | + } | ||
30 | + }, | ||
31 | + computed: { | ||
32 | + backgroundColorStyle() { | ||
33 | + return color.getBackgroundColorStyle(this.backgroundColor) | ||
34 | + }, | ||
35 | + backgroundColorClass() { | ||
36 | + return color.getBackgroundColorInternalClass(this.backgroundColor) | ||
37 | + }, | ||
38 | + fontColorStyle() { | ||
39 | + return color.getFontColorStyle(this.fontColor) | ||
40 | + }, | ||
41 | + fontColorClass() { | ||
42 | + return color.getFontColorInternalClass(this.fontColor) | ||
43 | + }, | ||
44 | + fontSizeStyle() { | ||
45 | + string.getLengthUnitValue(this.fontSize, this.fontUnit) | ||
46 | + } | ||
47 | + }, | ||
48 | + methods: { | ||
49 | + | ||
50 | + } | ||
51 | +} |
uni_modules/tn-popup/package.json
0 → 100644
1 | +{ | ||
2 | + "id": "tn-popup", | ||
3 | + "displayName": "tn-popup 弹出层 全面兼容vue2、app、h5、wx小程序", | ||
4 | + "version": "1.0.0", | ||
5 | + "description": "弹出层容器,用于展示弹窗、信息提示等内容,支持上、下、左、右和中部弹出。组件只提供容器,内部内容由用户自定义", | ||
6 | + "keywords": [ | ||
7 | + "tuniaoui", | ||
8 | + "tuniao", | ||
9 | + "tn-popup", | ||
10 | + "popup", | ||
11 | + "弹出层" | ||
12 | +], | ||
13 | + "repository": "", | ||
14 | + "engines": { | ||
15 | + "HBuilderX": "^3.1.0" | ||
16 | + }, | ||
17 | + "dcloudext": { | ||
18 | + "type": "component-vue", | ||
19 | + "sale": { | ||
20 | + "regular": { | ||
21 | + "price": "0.00" | ||
22 | + }, | ||
23 | + "sourcecode": { | ||
24 | + "price": "0.00" | ||
25 | + } | ||
26 | + }, | ||
27 | + "contact": { | ||
28 | + "qq": "" | ||
29 | + }, | ||
30 | + "declaration": { | ||
31 | + "ads": "无", | ||
32 | + "data": "无", | ||
33 | + "permissions": "无" | ||
34 | + }, | ||
35 | + "npmurl": "" | ||
36 | + }, | ||
37 | + "uni_modules": { | ||
38 | + "dependencies": [], | ||
39 | + "encrypt": [], | ||
40 | + "platforms": { | ||
41 | + "cloud": { | ||
42 | + "tcb": "y", | ||
43 | + "aliyun": "y", | ||
44 | + "alipay": "y" | ||
45 | + }, | ||
46 | + "client": { | ||
47 | + "Vue": { | ||
48 | + "vue2": "y", | ||
49 | + "vue3": "u" | ||
50 | + }, | ||
51 | + "App": { | ||
52 | + "app-vue": "y", | ||
53 | + "app-nvue": "u", | ||
54 | + "app-uvue": "u" | ||
55 | + }, | ||
56 | + "H5-mobile": { | ||
57 | + "Safari": "y", | ||
58 | + "Android Browser": "y", | ||
59 | + "微信浏览器(Android)": "y", | ||
60 | + "QQ浏览器(Android)": "y" | ||
61 | + }, | ||
62 | + "H5-pc": { | ||
63 | + "Chrome": "y", | ||
64 | + "IE": "y", | ||
65 | + "Edge": "y", | ||
66 | + "Firefox": "y", | ||
67 | + "Safari": "y" | ||
68 | + }, | ||
69 | + "小程序": { | ||
70 | + "微信": "y", | ||
71 | + "阿里": "u", | ||
72 | + "百度": "u", | ||
73 | + "字节跳动": "u", | ||
74 | + "QQ": "u", | ||
75 | + "钉钉": "u", | ||
76 | + "快手": "u", | ||
77 | + "飞书": "u", | ||
78 | + "京东": "u" | ||
79 | + }, | ||
80 | + "快应用": { | ||
81 | + "华为": "u", | ||
82 | + "联盟": "u" | ||
83 | + } | ||
84 | + } | ||
85 | + } | ||
86 | + } | ||
87 | +} |
uni_modules/tn-popup/readme.md
0 → 100644
1 | +# Popup 弹出层 | ||
2 | +> 组件名: tn-popup | ||
3 | +> 弹出层容器,用于展示弹窗、信息提示等内容,支持上、下、左、右和中部弹出。组件只提供容器,内部内容由用户自定义。 | ||
4 | +# 提示 | ||
5 | +> popup弹出层中对应自定义内容的容器中已经内置了scroll-view元素,内如内容超出容器的高度,将会自动获得垂直滚动的特性,如果您因为在slot内容做了滚动的处理,而造成了 冲突的话,请移除自定义关于滚动部分的逻辑。 | ||
6 | +# [查看文档](https://vue2.tuniaokj.com/components/popup.html) | ||
7 | +## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=tuniao-tuniaoui) | ||
8 | +## [问题反馈/提出建议](https://vue2.tuniaokj.com/cooperation/about.html) | ||
9 | + |
uni_modules/tn-popup/string.js
0 → 100644
1 | +/** | ||
2 | + * 去掉字符串中空格 | ||
3 | + * | ||
4 | + * @param {String} str 待处理的字符串 | ||
5 | + * @param {String} type 处理类型 | ||
6 | + */ | ||
7 | +function trim(str, type = 'both') { | ||
8 | + if (type === 'both') { | ||
9 | + return str.replace(/^\s+|\s+$/g, "") | ||
10 | + } else if (type === 'left') { | ||
11 | + return str.replace(/^\s*/g, "") | ||
12 | + } else if (type === 'right') { | ||
13 | + return str.replace(/(\s*$)/g, "") | ||
14 | + } else if (type === 'all') { | ||
15 | + return str.replace(/\s+/g, "") | ||
16 | + } else { | ||
17 | + return str | ||
18 | + } | ||
19 | +} | ||
20 | + | ||
21 | +/** | ||
22 | + * 获取带单位的长度值 | ||
23 | + * | ||
24 | + * @param {String} value 待处理的值 | ||
25 | + * @param {String} unit 单位 | ||
26 | + */ | ||
27 | +function getLengthUnitValue(value, unit = 'rpx') { | ||
28 | + if (!value) { | ||
29 | + return '' | ||
30 | + } | ||
31 | + if (/(%|px|rpx|auto)$/.test(value)) return value | ||
32 | + else return value + unit | ||
33 | +} | ||
34 | + | ||
35 | +/** | ||
36 | + * 将驼峰命名的字符串转换为指定连接符来进行连接 | ||
37 | + * | ||
38 | + * @param {Object} string 待转换的字符串 | ||
39 | + * @param {Object} replace 进行连接的字符 | ||
40 | + */ | ||
41 | +function humpConvertChar(string, replace = '_') { | ||
42 | + if (!string || !replace) { | ||
43 | + return '' | ||
44 | + } | ||
45 | + return string.replace(/([A-Z])/g, `${replace}$1`).toLowerCase() | ||
46 | +} | ||
47 | + | ||
48 | +/** | ||
49 | + * 将用指定连接符来进行连接的字符串转为驼峰命名的字符串 | ||
50 | + * | ||
51 | + * @param {Object} string 待转换的字符串 | ||
52 | + * @param {Object} replace 进行连接的字符 | ||
53 | + */ | ||
54 | +function charConvertHump(string, replace = '_') { | ||
55 | + if (!string || !replace) { | ||
56 | + return '' | ||
57 | + } | ||
58 | + let reg = RegExp(replace + "(\\w)", "g") | ||
59 | + return string.replace(reg, function(all, letter) { | ||
60 | + return letter.toUpperCase() | ||
61 | + }) | ||
62 | +} | ||
63 | + | ||
64 | +export default { | ||
65 | + trim, | ||
66 | + getLengthUnitValue, | ||
67 | + humpConvertChar, | ||
68 | + charConvertHump | ||
69 | +} |
uni_modules/tn-popup/theme.scss
0 → 100644
1 | +// 此文件为TuniaoUI的主题变量,这些变量目前只能通过uni.scss引入才有效,另外由于 | ||
2 | +// uni.scss中引入的样式会同时混入到全局样式文件和单独每一个页面的样式中,造成微信程序包太大, | ||
3 | +// 故uni.scss只建议放scss变量名相关样式,其他的样式可以通过main.js或者App.vue引入 | ||
4 | + | ||
5 | +// 组件配置 | ||
6 | +$tn-form-item-height: 70rpx; | ||
7 | + | ||
8 | + | ||
9 | +// 主颜色 | ||
10 | +$tn-main-color: #01BEFF; | ||
11 | +$tn-main-orange: #FBBD12; | ||
12 | +$tn-embellished-green: #00FFC6; | ||
13 | +$tn-embellished-yellow: #FFF00D; | ||
14 | +$tn-auxiliary-powder: #FF71D2; | ||
15 | +$tn-auxiliary-blue: #82B2FF; | ||
16 | +$tn-bg-color: #FFFFFF; | ||
17 | +$tn-bg-gray-color: #F4F4F4; | ||
18 | +$tn-space-color: #F8F7F8; | ||
19 | + | ||
20 | +// 边框颜色 | ||
21 | +$tn-border-solid-color: rgba(0, 0, 0, 0.1); | ||
22 | +$tn-border-solids-color: #eee; | ||
23 | +$tn-border-dashed-color: #ddd; | ||
24 | + | ||
25 | +// 阴影颜色 | ||
26 | +$tn-shadow-color: rgba(0, 0, 0, 0.1); | ||
27 | +$tn-box-shadow-color: rgba(0, 0, 0, 0.2); | ||
28 | + | ||
29 | +// 字体颜色 | ||
30 | +$tn-font-color: #080808; | ||
31 | +$tn-font-sub-color: #AAAAAA; | ||
32 | +$tn-content-color: #838383; | ||
33 | +$tn-font-holder-color: #E6E6E6; | ||
34 | + | ||
35 | +$tn-mask-bg-color: rgba(0, 0, 0, 0.4); | ||
36 | + | ||
37 | +$tn-progress-bg-color: #f0f0f0; | ||
38 | + | ||
39 | + | ||
40 | +$tn-color-red: #E83A30; | ||
41 | +$tn-color-red-dark: #BA2E26; | ||
42 | +$tn-color-red-disabled: #F39C97; | ||
43 | +$tn-color-red-light: #FAD8D6; | ||
44 | + | ||
45 | +$tn-color-purplered: #E72F8C; | ||
46 | +$tn-color-purplered-dark: #B9266F; | ||
47 | +$tn-color-purplered-disabled: #F397C5; | ||
48 | +$tn-color-purplered-light: #FAD5E8; | ||
49 | + | ||
50 | +$tn-color-purple: #892FE8; | ||
51 | +$tn-color-purple-dark: #6E26BA; | ||
52 | +$tn-color-purple-disabled: #C497F3; | ||
53 | +$tn-color-purple-light: #E7D5FA; | ||
54 | + | ||
55 | +$tn-color-bluepurple: #5F4FD9; | ||
56 | +$tn-color-bluepurple-dark: #4C3FAE; | ||
57 | +$tn-color-bluepurple-disabled: #AFA7EC; | ||
58 | +$tn-color-bluepurple-light: #DFDCF7; | ||
59 | + | ||
60 | +$tn-color-aquablue: #3646FF; | ||
61 | +$tn-color-aquablue-dark: #2B38CC; | ||
62 | +$tn-color-aquablue-disabled: #9AA2FF; | ||
63 | +$tn-color-aquablue-light: #D7DAFF; | ||
64 | + | ||
65 | +$tn-color-blue: #3D7EFF; | ||
66 | +$tn-color-blue-dark: #3165CC; | ||
67 | +$tn-color-blue-disabled: #9EBEFF; | ||
68 | +$tn-color-blue-light: #D8E5FF; | ||
69 | + | ||
70 | +$tn-color-indigo: #31C9E8; | ||
71 | +$tn-color-indigo-dark: #27A1BA; | ||
72 | +$tn-color-indigo-disabled: #98E4F3; | ||
73 | +$tn-color-indigo-light: #D6F4FA; | ||
74 | + | ||
75 | +$tn-color-cyan: #2DE8BD; | ||
76 | +$tn-color-cyan-dark: #24BA97; | ||
77 | +$tn-color-cyan-disabled: #96F3DE; | ||
78 | +$tn-color-cyan-light: #D5FAF2; | ||
79 | + | ||
80 | +$tn-color-teal: #24F083; | ||
81 | +$tn-color-teal-dark: #1DC069; | ||
82 | +$tn-color-teal-disabled: #91F7C1; | ||
83 | +$tn-color-teal-light: #D3FCE6; | ||
84 | + | ||
85 | +$tn-color-green: #31E749; | ||
86 | +$tn-color-green-dark: #27B93A; | ||
87 | +$tn-color-green-disabled: #98F3A4; | ||
88 | +$tn-color-green-light: #D6FADB; | ||
89 | + | ||
90 | +$tn-color-yellowgreen: #A4E82F; | ||
91 | +$tn-color-yellowgreen-dark: #82BA26; | ||
92 | +$tn-color-yellowgreen-disabled: #D1F397; | ||
93 | +$tn-color-yellowgreen-light: #EDFAD5; | ||
94 | + | ||
95 | +$tn-color-lime: #D5EB00; | ||
96 | +$tn-color-lime-dark: #AABC00; | ||
97 | +$tn-color-lime-disabled: #E9F57F; | ||
98 | +$tn-color-lime-light: #F7FBCC; | ||
99 | + | ||
100 | +$tn-color-yellow: #FFF420; | ||
101 | +$tn-color-yellow-dark: #CCC21A; | ||
102 | +$tn-color-yellow-disabled: #FFF88F; | ||
103 | +$tn-color-yellow-light: #FFFDD2; | ||
104 | + | ||
105 | +$tn-color-orangeyellow: #FFCA28; | ||
106 | +$tn-color-orangeyellow-dark: #CCA220; | ||
107 | +$tn-color-orangeyellow-disabled: #FFE493; | ||
108 | +$tn-color-orangeyellow-light: #FFF4D4; | ||
109 | + | ||
110 | +$tn-color-orange: #FFA726; | ||
111 | +$tn-color-orange-dark: #CC851E; | ||
112 | +$tn-color-orange-disabled: #FFD392; | ||
113 | +$tn-color-orange-light: #FFEDD4 ; | ||
114 | + | ||
115 | +$tn-color-orangered: #FF7043; | ||
116 | +$tn-color-orangered-dark: #CC5A36; | ||
117 | +$tn-color-orangered-disabled: #FFB7A1; | ||
118 | +$tn-color-orangered-light: #FFE2D9; | ||
119 | + | ||
120 | +$tn-color-brown: #914F2C; | ||
121 | +$tn-color-brown-dark: #743F23; | ||
122 | +$tn-color-brown-disabled: #C8A795; | ||
123 | +$tn-color-brown-light: #E9DCD5; | ||
124 | + | ||
125 | +$tn-color-grey: #78909C; | ||
126 | +$tn-color-grey-dark: #5F7E8B; | ||
127 | +$tn-color-grey-disabled: #C6D1D8; | ||
128 | +$tn-color-grey-light: #E4E9EC; | ||
129 | + | ||
130 | +$tn-color-gray: #AAAAAA; | ||
131 | +$tn-color-gray-dark: #838383; | ||
132 | +$tn-color-gray-disabled: #E6E6E6; | ||
133 | +$tn-color-gray-light: #F8F7F8; | ||
134 | + | ||
135 | +$tn-cool-bg-color-1-start: #F5317F; | ||
136 | +$tn-cool-bg-color-1-end: #FF7C6E; | ||
137 | + | ||
138 | +$tn-cool-bg-color-2-start: #CA26FF; | ||
139 | +$tn-cool-bg-color-2-end: #F360A7; | ||
140 | + | ||
141 | +$tn-cool-bg-color-3-start: #A26FFC; | ||
142 | +$tn-cool-bg-color-3-end: #9D12FF; | ||
143 | + | ||
144 | +$tn-cool-bg-color-4-start: #AA77F0; | ||
145 | +$tn-cool-bg-color-4-end: #E871E5; | ||
146 | + | ||
147 | +$tn-cool-bg-color-5-start: #40A0F7; | ||
148 | +$tn-cool-bg-color-5-end: #4866E6; | ||
149 | + | ||
150 | +$tn-cool-bg-color-6-start: #209CFF; | ||
151 | +$tn-cool-bg-color-6-end: #68E0CF; | ||
152 | + | ||
153 | +$tn-cool-bg-color-7-start: #00C3FF; | ||
154 | +$tn-cool-bg-color-7-end: #58FFF5; | ||
155 | + | ||
156 | +$tn-cool-bg-color-8-start: #00d1FF; | ||
157 | +$tn-cool-bg-color-8-end: #69FF97; | ||
158 | + | ||
159 | +$tn-cool-bg-color-9-start: #0FD893; | ||
160 | +$tn-cool-bg-color-9-end: #29ECBF; | ||
161 | + | ||
162 | +$tn-cool-bg-color-10-start: #0FD850; | ||
163 | +$tn-cool-bg-color-10-end: #F9F047; | ||
164 | + | ||
165 | +$tn-cool-bg-color-11-start: #24FE41; | ||
166 | +$tn-cool-bg-color-11-end: #F7FD47; | ||
167 | + | ||
168 | +$tn-cool-bg-color-12-start: #D6FF7F; | ||
169 | +$tn-cool-bg-color-12-end: #00F657; | ||
170 | + | ||
171 | +$tn-cool-bg-color-13-start: #FA709A; | ||
172 | +$tn-cool-bg-color-13-end: #FEE140; | ||
173 | + | ||
174 | +$tn-cool-bg-color-14-start: #FE5E9C; | ||
175 | +$tn-cool-bg-color-14-end: #F1AA76; | ||
176 | + | ||
177 | +$tn-cool-bg-color-15-start: #FF3181; | ||
178 | +$tn-cool-bg-color-15-end: #FF8331; | ||
179 | + | ||
180 | +$tn-cool-bg-color-16-start: #ED1C24; | ||
181 | +$tn-cool-bg-color-16-end: #FECE12; | ||
182 | + | ||
183 | + |