Commit 3972b9236af55ef4156468e34d280ab1b528144b

Authored by guzijian
1 parent 98c47411

feat: 优化获取验证码界面,优化工具类方法,优化u-message-input在ios端输入问题

garbage-removal/.env.production
1   -# VITE_BASE_URL=http://1.14.107.94:8820
  1 +VITE_BASE_URL=http://101.95.0.106:10001/workflow
2 2 # VITE_BASE_FILE_UPLOAD_PREFIX=/common/upload
3 3 # VITE_STOMP_URL=http://localhost:8860/ws
... ...
garbage-removal/src/App.vue
... ... @@ -8,14 +8,9 @@ onLaunch(async () => {
8 8 console.log("onLaunch");
9 9 if (store.token) {
10 10 setRequestToken(store.token);
11   - // store.userInfo = (await getUserInfo()).data.data;
12 11 uni.switchTab({
13 12 url: "/pages/home/index",
14 13 });
15   - } else {
16   - uni.reLaunch({
17   - url: "/pages/login/index",
18   - });
19 14 }
20 15 });
21 16 </script>
... ...
garbage-removal/src/apis/user.js
... ... @@ -15,6 +15,16 @@ export async function userLogin( params, config) {
15 15 }
16 16  
17 17 /**
  18 + * @method 退出登录
  19 + */
  20 +export async function loginOut( params, config) {
  21 + return await request.post(
  22 + `${prefix}/logout`,
  23 + );
  24 +}
  25 +
  26 +
  27 +/**
18 28 * @method 获取用户信息
19 29 */
20 30 export async function getUserInfo(config) {
... ...
garbage-removal/src/components/uni-combox/index.vue 0 → 100644
  1 +<template>
  2 + <view class="uni-combox" :class="border ? '' : 'uni-combox__no-border'">
  3 + <view v-if="label" class="uni-combox__label" :style="labelStyle">
  4 + <text>{{ label }}</text>
  5 + </view>
  6 + <view class="uni-combox__input-box">
  7 + <input class="uni-combox__input" type="text" :placeholder="placeholder" placeholder-class="uni-combox__input-plac"
  8 + v-model="inputVal" @input="onInput" @focus="onFocus" @blur="onBlur" />
  9 + <uni-icons :type="showSelector ? 'top' : 'bottom'" size="14" color="#999" @click="toggleSelector">
  10 + </uni-icons>
  11 + </view>
  12 + <view class="uni-combox__selector" v-if="showSelector">
  13 + <view class="uni-popper__arrow"></view>
  14 + <scroll-view scroll-y="true" class="uni-combox__selector-scroll">
  15 + <view class="uni-combox__selector-empty" v-if="filterCandidatesLength === 0">
  16 + <text>{{ emptyTips }}</text>
  17 + </view>
  18 + <view class="uni-combox__selector-item" v-for="(item, index) in filterCandidates" :key="index"
  19 + @click="onSelectorClick(index)">
  20 + <text>{{ item }}</text>
  21 + </view>
  22 + </scroll-view>
  23 + </view>
  24 + </view>
  25 +</template>
  26 +
  27 +<script>
  28 +/**
  29 + * Combox 组合输入框
  30 + * @description 组合输入框一般用于既可以输入也可以选择的场景
  31 + * @tutorial https://ext.dcloud.net.cn/plugin?id=1261
  32 + * @property {String} label 左侧文字
  33 + * @property {String} labelWidth 左侧内容宽度
  34 + * @property {String} placeholder 输入框占位符
  35 + * @property {Array} candidates 候选项列表
  36 + * @property {String} emptyTips 筛选结果为空时显示的文字
  37 + * @property {String} value 组合框的值
  38 + */
  39 +export default {
  40 + name: 'uniCombox',
  41 + emits: ['input', 'update:modelValue'],
  42 + props: {
  43 + border: {
  44 + type: Boolean,
  45 + default: true
  46 + },
  47 + label: {
  48 + type: String,
  49 + default: ''
  50 + },
  51 + labelWidth: {
  52 + type: String,
  53 + default: 'auto'
  54 + },
  55 + placeholder: {
  56 + type: String,
  57 + default: ''
  58 + },
  59 + candidates: {
  60 + type: Array,
  61 + default() {
  62 + return []
  63 + }
  64 + },
  65 + emptyTips: {
  66 + type: String,
  67 + default: '无匹配项'
  68 + },
  69 + // #ifndef VUE3
  70 + value: {
  71 + type: [String, Number],
  72 + default: ''
  73 + },
  74 + // #endif
  75 + // #ifdef VUE3
  76 + modelValue: {
  77 + type: [String, Number],
  78 + default: ''
  79 + },
  80 + // #endif
  81 + },
  82 + data() {
  83 + return {
  84 + showSelector: false,
  85 + inputVal: ''
  86 + }
  87 + },
  88 + computed: {
  89 + labelStyle() {
  90 + if (this.labelWidth === 'auto') {
  91 + return ""
  92 + }
  93 + return `width: ${this.labelWidth}`
  94 + },
  95 + filterCandidates() {
  96 + return this.candidates.filter((item) => {
  97 + return item.toString().indexOf(this.inputVal) > -1
  98 + })
  99 + },
  100 + filterCandidatesLength() {
  101 + return this.filterCandidates.length
  102 + }
  103 + },
  104 + watch: {
  105 + // #ifndef VUE3
  106 + value: {
  107 + handler(newVal) {
  108 + this.inputVal = newVal
  109 + },
  110 + immediate: true
  111 + },
  112 + // #endif
  113 + // #ifdef VUE3
  114 + modelValue: {
  115 + handler(newVal) {
  116 + this.inputVal = newVal
  117 + },
  118 + immediate: true
  119 + },
  120 + // #endif
  121 + },
  122 + methods: {
  123 + toggleSelector() {
  124 + this.showSelector = !this.showSelector
  125 + },
  126 + onFocus() {
  127 + this.showSelector = true
  128 + },
  129 + onBlur() {
  130 + setTimeout(() => {
  131 + this.showSelector = false
  132 + }, 153)
  133 + },
  134 + onSelectorClick(index) {
  135 + this.inputVal = this.filterCandidates[index]
  136 + this.showSelector = false
  137 + this.$emit('input', this.inputVal)
  138 + this.$emit('update:modelValue', this.inputVal)
  139 + },
  140 + onInput() {
  141 + setTimeout(() => {
  142 + this.$emit('input', this.inputVal)
  143 + this.$emit('update:modelValue', this.inputVal)
  144 + })
  145 + }
  146 + }
  147 +}
  148 +</script>
  149 +
  150 +<style lang="scss" scoped>
  151 +.uni-combox {
  152 + font-size: 14px;
  153 + border: 1px solid #DCDFE6;
  154 + border-radius: 4px;
  155 + padding: 6px 10px;
  156 + position: relative;
  157 + /* #ifndef APP-NVUE */
  158 + display: flex;
  159 + /* #endif */
  160 + // height: 40px;
  161 + flex-direction: row;
  162 + align-items: center;
  163 + // border-bottom: solid 1px #DDDDDD;
  164 + color: $u-info;
  165 +}
  166 +
  167 +.uni-combox__label {
  168 + font-size: 16px;
  169 + line-height: 22px;
  170 + padding-right: 10px;
  171 + color: #999999;
  172 +}
  173 +
  174 +.uni-combox__input-box {
  175 + position: relative;
  176 + /* #ifndef APP-NVUE */
  177 + display: flex;
  178 + /* #endif */
  179 + flex: 1;
  180 + flex-direction: row;
  181 + align-items: center;
  182 +}
  183 +
  184 +.uni-combox__input {
  185 + flex: 1;
  186 + font-size: 14px;
  187 + height: 22px;
  188 + line-height: 22px;
  189 + color: $u-info;
  190 +}
  191 +
  192 +.uni-combox__input-plac {
  193 + font-size: 14px;
  194 + color: #999;
  195 +}
  196 +
  197 +.uni-combox__selector {
  198 + /* #ifndef APP-NVUE */
  199 + box-sizing: border-box;
  200 + /* #endif */
  201 + position: absolute;
  202 + top: calc(100% + 12px);
  203 + left: 0;
  204 + width: 100%;
  205 + background-color: #FFFFFF;
  206 + border: 1px solid #EBEEF5;
  207 + border-radius: 6px;
  208 + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  209 + z-index: 2;
  210 + padding: 4px 0;
  211 +}
  212 +
  213 +.uni-combox__selector-scroll {
  214 + /* #ifndef APP-NVUE */
  215 + max-height: 200px;
  216 + box-sizing: border-box;
  217 + /* #endif */
  218 +}
  219 +
  220 +.uni-combox__selector-empty,
  221 +.uni-combox__selector-item {
  222 + /* #ifndef APP-NVUE */
  223 + display: flex;
  224 + cursor: pointer;
  225 + /* #endif */
  226 + line-height: 36px;
  227 + font-size: 14px;
  228 + text-align: center;
  229 + // border-bottom: solid 1px #DDDDDD;
  230 + padding: 0px 10px;
  231 +}
  232 +
  233 +.uni-combox__selector-item:hover {
  234 + background-color: #f9f9f9;
  235 +}
  236 +
  237 +.uni-combox__selector-empty:last-child,
  238 +.uni-combox__selector-item:last-child {
  239 + /* #ifndef APP-NVUE */
  240 + border-bottom: none;
  241 + /* #endif */
  242 +}
  243 +
  244 +// picker 弹出层通用的指示小三角
  245 +.uni-popper__arrow,
  246 +.uni-popper__arrow::after {
  247 + position: absolute;
  248 + display: block;
  249 + width: 0;
  250 + height: 0;
  251 + border-color: transparent;
  252 + border-style: solid;
  253 + border-width: 6px;
  254 +}
  255 +
  256 +.uni-popper__arrow {
  257 + filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
  258 + top: -6px;
  259 + left: 10%;
  260 + margin-right: 3px;
  261 + border-top-width: 0;
  262 + border-bottom-color: #EBEEF5;
  263 +}
  264 +
  265 +.uni-popper__arrow::after {
  266 + content: " ";
  267 + top: 1px;
  268 + margin-left: -6px;
  269 + border-top-width: 0;
  270 + border-bottom-color: #fff;
  271 +}
  272 +
  273 +.uni-combox__no-border {
  274 + border: none;
  275 +}
  276 +</style>
... ...
garbage-removal/src/pages/home/clean/index.vue
... ... @@ -3,15 +3,14 @@
3 3 <view class="company-clean-container-box">
4 4 <view class="company-clean-container-header">
5 5 <view class="company-clean-container-header-address">
6   - {{ userAddress.garUserAddress + userAddress.garRemark }}
  6 + {{ userAddress.garUserAddress }}{{ userAddress.garRemark }}
7 7 </view>
8 8 <view class="company-clean-container-header-base-info">
9 9 {{ userAddress.garUserContactName }} {{ userAddress.garUserContactTel }}
10 10 </view>
11 11 <view class="company-clean-container-header-reservation">
12 12 <view class="company-clean-container-header-reservation-left">
13   - <u-icon name="calendar" size="40"></u-icon>
14   - 预约时间
  13 + <text style="color: red;">*</text> <u-icon name="calendar" size="40"></u-icon>预约时间
15 14 </view>
16 15 <view class="company-clean-container-header-reservation-right">
17 16 <text style="margin-right: 10rpx;" @click="handleTimeChoose">{{ dayTime ? dayTime : "请选择时间" }}</text> <u-icon
... ... @@ -21,28 +20,31 @@
21 20 </view>
22 21 </view>
23 22 <view class="company-clean-container-car-main">
24   - <view class="company-clean-container-car-main-title">
  23 + <!-- <view class="company-clean-container-car-main-title">
25 24 自卸车
26   - </view>
  25 + </view> -->
27 26 <view class="company-clean-container-car-main-content">
28   - <view class="company-clean-container-car-main-content-img">
  27 + <view class="company-clean-container-car-main-content-type">
  28 + <text class="company-clean-container-car-main-content-type-price-area"><text
  29 + style="color: red;">*</text>垃圾类型:</text>
  30 + <uni-combox label="" :candidates="garbageTypeList" placeholder="请选择垃圾类型"
  31 + v-model="paramFrom.garbageType"></uni-combox>
  32 + </view>
  33 + <!-- <view class="company-clean-container-car-main-content-img">
29 34 <image class="company-clean-container-car-main-content-img" />
30 35 </view>
31 36 <view class="company-clean-container-car-main-content-remark">
32 37 箱体长4.2m宽1.1,高0.7,最多课容纳约110袋袋装修垃圾(75cm*45每袋)
33   - </view>
  38 + </view> -->
34 39 <view class="company-clean-container-car-main-content-type">
35   - <view class="company-clean-container-car-main-content-type-choose">
36   - <text style="margin-right: 10rpx;">6方车</text>
37   - <u-icon name="arrow-down" size="30" color="#ffffff"></u-icon>
38   - </view>
39   - <view class="company-clean-container-car-main-content-type-price-area">
40   - 车
41   - </view>
  40 + <text class="company-clean-container-car-main-content-type-price-area"><text
  41 + style="color: red;">*</text>车辆类型:</text>
  42 + <uni-combox label="" :candidates="candidates" placeholder="请选择运输车辆类型"
  43 + v-model="paramFrom.carType"></uni-combox>
42 44 </view>
43 45 <view class="company-clean-container-car-main-content-number">
44 46 <view class="company-clean-container-car-main-content-number-txt">
45   - 添加车辆数量
  47 + <text style="color: red;">*</text>添加车辆数量
46 48 </view>
47 49 <view class="company-clean-container-car-main-content-number-button">
48 50 <u-number-box :min="1" integer buttonSize="46" v-model="paramFrom.carNumber"></u-number-box>
... ... @@ -72,8 +74,8 @@
72 74 <view class="company-clean-container-site-image-info-sure-button">
73 75 <view class="company-clean-container-site-image-info-sure-button-radio">
74 76 <u-radio-group v-model="paramFrom.sureReadFlag">
75   - <u-radio activeColor="#a9e08f" size="27" labelSize="25" :labelDisabled="true" labelColor="#909399"
76   - label=""></u-radio>本人已确认信息真实有效,并将上述信息告知市容环境卫生主管部门。
  77 + <u-radio activeColor="#a9e08f" size="27" labelSize="25" :name="true" :labelDisabled="true"
  78 + labelColor="#909399" label=""></u-radio>本人已确认信息真实有效,并将上述信息告知市容环境卫生主管部门。
77 79 </u-radio-group>
78 80 </view>
79 81 </view>
... ... @@ -103,6 +105,7 @@ import { queryCarList } from &#39;@/apis/carinfo.js&#39;;
103 105 import { uploadFilePromise } from '@/apis/common.js';
104 106 import { saveOrder } from '@/apis/order.js';
105 107 import liuDeliveryTime from "@/components/liu-delivery-time/liu-delivery-time.vue";
  108 +import uniCombox from "@/components/uni-combox/index.vue";
106 109 import { onLoad } from '@dcloudio/uni-app';
107 110 import { getCurrentInstance, onMounted, ref } from 'vue';
108 111 const { proxy } = getCurrentInstance();
... ... @@ -111,18 +114,25 @@ const y = ref()
111 114 const movableAreaElement = ref()
112 115 const companyObj = ref()
113 116 const tel = ref()
114   -const userAddress = ref()
115   -const carList = ref();
  117 +const userAddress = ref({
  118 + garUserContactName: "",
  119 + garUserContactTel: "",
  120 + garRemark: "",
  121 + garUserAddress: ""
  122 +})
  123 +const garbageTypeList = ref(["建筑垃圾", "装修垃圾"])
116 124 const paramFrom = ref({
117   - carNumber: 0,
  125 + carNumber: 1,
118 126 remark: "",
119   - sureReadFlag: false
  127 + sureReadFlag: false,
  128 + carType: "",
  129 + garbageType: "装修垃圾"
120 130 })
121 131 const dayTime = ref()
122 132  
123 133 const chooseTime = ref()
124 134 const fileList = ref([])
125   -
  135 +const candidates = ref([])
126 136 const handleTimeChoose = () => {
127 137 chooseTime.value.open();
128 138 }
... ... @@ -135,12 +145,12 @@ const changeTime = (e) =&gt; {
135 145 */
136 146 onLoad((options) => {
137 147 companyObj.value = JSON.parse(options.companyObj);
138   - console.log(companyObj.value);
139 148 tel.value = options.tel;
140 149 userAddress.value = JSON.parse(options.userAddress);
141   - queryCarList({ companyId: companyObj.value.id }.then(res => {
142   - carList.value = res.data.rows
143   - }))
  150 + queryCarList({ companyId: companyObj.value.id }).then(res => {
  151 + candidates.value = res.data.rows.map(item => item.carType)
  152 + console.log(candidates.value);
  153 + })
144 154 })
145 155  
146 156 /**
... ... @@ -188,14 +198,13 @@ const afterRead = async (event) =&gt; {
188 198 * 处理车子数量
189 199 */
190 200 const handleCarInfo = () => {
191   - console.log("车子数量");
  201 +
192 202 }
193 203  
194 204 /**
195 205 * 处理下单
196 206 */
197 207 const handleOderSure = () => {
198   - console.log("下单了", companyObj.value);
199 208 let params = {
200 209 /**
201 210 * 订单地址
... ... @@ -211,11 +220,13 @@ const handleOderSure = () =&gt; {
211 220 * 订单姓名
212 221 */
213 222 garOrderContactName: userAddress.value.garUserContactName,
  223 + garOrderCarNumber: paramFrom.value.carNumber,
  224 + garOrderCarType: paramFrom.value.carType,
214 225  
215 226 /**
216 227 * 垃圾类型
217 228 */
218   - garOrderTrashType: "建筑垃圾",
  229 + garOrderTrashType: paramFrom.value.garbageType,
219 230  
220 231 /**
221 232 * 订单人电话
... ... @@ -254,7 +265,6 @@ const handleOderSure = () =&gt; {
254 265 return;
255 266 }
256 267 saveOrder(params).then(res => {
257   - console.log(res);
258 268 // TODO 订单详情
259 269 if (res.data.success) {
260 270 uni.$u.route({
... ... @@ -284,6 +294,73 @@ onMounted(() =&gt; {
284 294 })
285 295  
286 296 const validateParams = (params) => {
  297 + if (!paramFrom.value.sureReadFlag) {
  298 + jumpPrompt('请勾选"本人已确认信息真实有效,并将上诉信息告知市容环境卫生主管部门"')
  299 + return false;
  300 + }
  301 + for (const key in params) {
  302 + if (!params[key] && key != "garRemark") {
  303 + switch (key) {
  304 + case "garOrderAgreementTime":
  305 + jumpPrompt('请选择预约时间')
  306 + break;
  307 + case "garOrderTrashType":
  308 + jumpPrompt('请选择垃圾类型')
  309 + break;
  310 + case "garOrderCarType":
  311 + jumpPrompt('请选择车辆类型')
  312 + break;
  313 + }
  314 + return false;
  315 + }
  316 + console.log("key:", key, key == "imageUrls");
  317 + if (key == "imageUrls") {
  318 + if (params[key].length == 0) {
  319 + jumpPrompt('请上传现场图片')
  320 + return false;
  321 + }
  322 + if (!validateImage(params[key])) {
  323 + uni.$u.toast('请等待图片上传完毕')
  324 + return false;
  325 + }
  326 + }
  327 + }
  328 +
  329 + return true;
  330 +}
  331 +
  332 +const jumpPrompt = (msg) => {
  333 + uni.showModal({
  334 + title: '提示',
  335 + content: msg,
  336 + showCancel: false,
  337 + success: function (res) {
  338 + if (res.confirm) {
  339 + } else if (res.cancel) {
  340 + }
  341 + }
  342 + });
  343 +}
  344 +const validateImage = (fileList) => {
  345 + console.log(fileList);
  346 +
  347 + for (let index = 0; index < fileList.length; index++) {
  348 + const str = fileList[index];
  349 + // #ifdef H5
  350 + if (str.startsWith("blob")) {
  351 + return false;
  352 + }
  353 + // #endif
  354 + // #ifdef MP-WEIXIN
  355 + if (uni.$u.test.contains(str, "/tmp/")) {
  356 + return false;
  357 + }
  358 + // #endif
  359 +
  360 + }
  361 + //
  362 + // #ifdef
  363 +
287 364 return true;
288 365 }
289 366  
... ... @@ -396,30 +473,15 @@ $custom-bottom-height: 200rpx;
396 473 display: flex;
397 474 justify-content: space-between;
398 475 border-radius: $custom-border-radio;
399   - background-color: $u-info-light;
  476 + // background-color: $u-info-light;
400 477 box-sizing: border-box;
401   - font-size: 30rpx;
402   -
403   - .company-clean-container-car-main-content-type-choose {
404   - width: 230rpx;
405   - color: #ffffff;
406   - display: flex;
407   - justify-content: center;
408   - align-items: center;
409   - background-color: #a9e08f;
410   - padding: 30rpx $custom-page-padding 30rpx $custom-page-padding;
411   - box-sizing: border-box;
412   - border-radius: $custom-border-radio 0 0 $custom-border-radio;
413   - }
414 478  
415 479 .company-clean-container-car-main-content-type-price-area {
416   - width: 100%;
417 480 display: flex;
418   - justify-content: flex-end;
  481 + justify-content: flex-start;
419 482 align-items: center;
420   - box-sizing: border-box;
421   - color: $u-tips-color;
422   - padding-right: $custom-page-padding;
  483 + color: $u-info;
  484 + white-space: nowrap;
423 485 }
424 486 }
425 487  
... ...
garbage-removal/src/pages/home/index.vue
... ... @@ -28,7 +28,7 @@
28 28  
29 29 <view class="company-container">
30 30 <span class="company-label-info">可进行垃圾清运单位</span>
31   - <view class="company-list-box">
  31 + <view class="company-list-box" v-if="addressInfo">
32 32 <view class="company-list-header">
33 33 <view class="company-list-header-left">
34 34 综合排序
... ... @@ -85,12 +85,26 @@
85 85 </view>
86 86 </view>
87 87 </z-paging>
88   -
89 88 </view>
90 89 </view>
91 90 </view>
  91 + <view class="company-empty" v-else @click="handleCleanClick">
  92 + <view class="empty-text-title">
  93 + 请先添加地址
  94 + </view>
  95 + <view class="empty-text-content">
  96 + 房屋装饰装修过程中产生的砖瓦、木材、熟料等废弃物
  97 + </view>
  98 + <view class="empty-text-box">
  99 + <view class="empty-text-box-button">
  100 + 立即清运
  101 + </view>
  102 + </view>
  103 + </view>
  104 +
92 105 </view>
93 106 </view>
  107 +
94 108 </view>
95 109 </template>
96 110  
... ... @@ -107,8 +121,6 @@ const topMargin = ref(null);//状态栏高度
107 121 const musicheadHeight = ref();
108 122 const paging = ref(null)
109 123 const swiperImageList = ref([{
110   - image: 'https://cdn.uviewui.com/uview/swiper/swiper2.png',
111   -}, {
112 124 image: 'https://cdn.uviewui.com/uview/swiper/swiper1.png',
113 125 }, {
114 126 image: 'https://cdn.uviewui.com/uview/swiper/swiper3.png',
... ... @@ -154,7 +166,21 @@ function handleAddressInfo() {
154 166 url: `pages/home/address/index`,
155 167 })
156 168 }
157   -
  169 +const handleCleanClick = () => {
  170 + uni.showModal({
  171 + title: '',
  172 + content: '请先添加清运地址',
  173 + success: function (res) {
  174 + if (res.confirm) {
  175 + uni.$u.route({
  176 + url: `pages/home/address/index`,
  177 + })
  178 + } else if (res.cancel) {
  179 + console.log('用户点击取消');
  180 + }
  181 + }
  182 + });
  183 +}
158 184 /**
159 185 * 公司详情
160 186 */
... ... @@ -209,7 +235,7 @@ const initData = () =&gt; {
209 235 userAddress.value = null;
210 236 // 查询用户当前地址
211 237 queryAddress('CURRENT').then(res => {
212   - if (res.data.data instanceof Array) {
  238 + if (res.data.data && res.data.data[0]) {
213 239 addressInfo.value = res.data.data[0].garUserAddress + res.data.data[0].garRemark
214 240 userAddress.value = JSON.stringify(res.data.data[0] ? res.data.data[0] : {});
215 241 }
... ... @@ -345,6 +371,45 @@ const queryList = (pageNo, pageSize) =&gt; {
345 371 font-size: 30rpx;
346 372 width: 100%;
347 373 color: $u-info-dark;
  374 +
  375 + }
  376 +
  377 + .company-empty {
  378 + width: 100%;
  379 + height: 300rpx;
  380 + box-sizing: border-box;
  381 + border-radius: 20rpx;
  382 + background: linear-gradient(to bottom, $u-primary, $u-primary-disabled);
  383 + // background-image: url("https://cdn.uviewui.com/uview/swiper/swiper2.png");
  384 + color: #ffffff;
  385 + background-size: 100% 100%;
  386 + padding: 20rpx;
  387 +
  388 + .empty-text-title {
  389 + font-size: 30rpx;
  390 + line-height: 60rpx;
  391 + }
  392 +
  393 + .empty-text-content {
  394 + font-size: small;
  395 + width: 60%;
  396 + line-height: 40rpx;
  397 + }
  398 +
  399 + .empty-text-box {
  400 + // width: 0%;
  401 + display: flex;
  402 +
  403 + .empty-text-box-button {
  404 + margin-top: 20rpx;
  405 + padding: 15rpx 30rpx 15rpx 30rpx;
  406 + white-space: nowrap;
  407 + box-sizing: border-box;
  408 + border-radius: 50rpx;
  409 + background-color: orange;
  410 + font-size: 25rpx;
  411 + }
  412 + }
348 413 }
349 414  
350 415 .company-list-box {
... ... @@ -485,5 +550,6 @@ const queryList = (pageNo, pageSize) =&gt; {
485 550 }
486 551 }
487 552 }
  553 +
488 554 }
489 555 </style>
... ...
garbage-removal/src/pages/login/code.vue
... ... @@ -3,7 +3,7 @@
3 3 <view class="key-input">
4 4 <view class="title">输入验证码</view>
5 5 <view class="tips">验证码已发送至 +{{ startIphoneNumber }}</view>
6   - <u-message-input :focus="true" :value="value" @change="change" @finish="finish" mode="bottomLine"
  6 + <u-message-input :value="value" :focus="true" @change="change" @finish="finish" mode="bottomLine"
7 7 :maxlength="maxlength"></u-message-input>
8 8 <text v-if="verifyFlag" class="error">验证码错误,请重新输入</text>
9 9 <view class="captcha">
... ... @@ -18,7 +18,8 @@
18 18 import { sendCode, userLogin } from "@/apis/user.js";
19 19 import { useMainStore } from '@/stores/index.js';
20 20 import { onLoad } from "@dcloudio/uni-app";
21   -import { ref } from "vue";
  21 +import { getCurrentInstance, ref } from "vue";
  22 +const { proxy } = getCurrentInstance();
22 23 const store = useMainStore();
23 24 const verifyFlag = ref(false)
24 25 const iphoneNumber = ref("")
... ... @@ -32,15 +33,7 @@ onLoad((options) =&gt; {
32 33 iphoneNumber.value = options.iphoneNumber;
33 34 startIphoneNumber.value = handleIphoneNumber("" + options.iphoneNumber);
34 35 getCaptcha(iphoneNumber.value)
35   - let interval = setInterval(() => {
36   - second.value--;
37   - if (second.value <= 0) {
38   - show.value = true
39   - clearInterval(interval);
40   - }
41   - }, 1000);
42 36 })
43   -
44 37 const handleIphoneNumber = (tel) => {
45 38 return tel.replace(tel.substring(3, 7), "****");
46 39 }
... ... @@ -49,6 +42,9 @@ const getCaptcha = (iphoneNumber) =&gt; {
49 42 value.value = ''
50 43 second.value = 10
51 44 show.value = false;
  45 + sendCode(iphoneNumber).then(res => {
  46 + proxy.$u.toast(res.data.data)
  47 + })
52 48 let interval = setInterval(() => {
53 49 second.value--;
54 50 if (second.value <= 0) {
... ... @@ -56,9 +52,7 @@ const getCaptcha = (iphoneNumber) =&gt; {
56 52 clearInterval(interval);
57 53 }
58 54 }, 1000);
59   - sendCode(iphoneNumber).then(res => {
60   - console.log(res);
61   - })
  55 +
62 56 }
63 57 // 收不到验证码选择时的选择
64 58 const noCaptcha = () => {
... ... @@ -80,7 +74,7 @@ const checkVerifyNum = (code) =&gt; {
80 74 if (res.data.success) {
81 75 verifyFlag.value = false;
82 76 store.tempToken = res.data.data
83   - uni.$u.route({
  77 + proxy.$u.route({
84 78 url: `pages/wode/choose/index`,
85 79 })
86 80 } else {
... ... @@ -91,6 +85,7 @@ const checkVerifyNum = (code) =&gt; {
91 85  
92 86 // change事件侦听
93 87 const change = (value) => {
  88 + // console.log("value:", value);
94 89 }
95 90  
96 91 // 输入完验证码最后一位执行
... ... @@ -115,6 +110,8 @@ const finish = (value) =&gt; {
115 110 .key-input {
116 111 padding: 30rpx 0;
117 112  
  113 + -webkit-user-select: text !important;
  114 +
118 115 text {
119 116 display: none;
120 117 }
... ...
garbage-removal/src/pages/login/index.vue
... ... @@ -34,7 +34,7 @@ import { userLogin } from &quot;@/apis/user.js&quot;;
34 34 export default {
35 35 data() {
36 36 return {
37   - tel: '18980249160'
  37 + tel: '18977778888'
38 38 }
39 39 },
40 40 computed: {
... ...
garbage-removal/src/pages/order/detail/index.vue
... ... @@ -27,7 +27,6 @@
27 27 {{ dataGram.garOrderAddress + dataGram.garOrderAddressDetails }}
28 28 </view>
29 29 </view>
30   -
31 30 <view class="order-detail-container-header-item">
32 31 <text class="order-detail-container-header-title">现场图片:</text>
33 32 <view class="order-detail-container-header-content">
... ... @@ -42,13 +41,19 @@
42 41 </view>
43 42 </view>
44 43 <view class="order-detail-container-header-item">
  44 + <text class="order-detail-container-header-title">车辆类型:</text>
  45 + <view class="order-detail-container-header-content">
  46 + {{ dataGram.garOrderCarType }}
  47 + </view>
  48 + </view>
  49 + <view class="order-detail-container-header-item">
45 50 <text class="order-detail-container-header-title">垃圾类型:</text>
46 51 <view class="order-detail-container-header-content">
47 52 {{ dataGram.garOrderTrashType }}
48 53 </view>
49 54 </view>
50 55 <view class="order-detail-container-header-item">
51   - <text class="order-detail-container-header-title">单号:</text>
  56 + <text class="order-detail-container-header-title">单号:</text>
52 57 <view class="order-detail-container-header-content">
53 58 {{ orderId }}
54 59 </view>
... ... @@ -75,7 +80,7 @@
75 80 <text class="order-detail-container-header-title">联系电话:</text>
76 81 <view class="order-detail-container-header-content">
77 82 {{ dataGram.garOrderContactTel }}
78   - <u-icon name="phone" size="28" @click="handleContactClick(dataGram.garOrderCompanyTel)"></u-icon>
  83 + <u-icon name="phone" size="28" @click="handleContactClick(dataGram.garOrderContactTel)"></u-icon>
79 84 </view>
80 85 </view>
81 86 <view class="order-detail-container-header-item">
... ... @@ -96,35 +101,42 @@
96 101 <view class="order-detail-container-header-card-title">
97 102 处理信息
98 103 </view>
99   - <view class="order-detail-container-header-item">
100   - <text class="order-detail-container-header-title">负责人:</text>
101   - <view class="order-detail-container-header-content">
102   - {{ dataGram.garOrderHandlerId }}
  104 + <view v-if="dataGram.garOrderHandlerId" style="width: 100%;">
  105 + <view class="order-detail-container-header-item">
  106 + <text class="order-detail-container-header-title">负责人:</text>
  107 + <view class="order-detail-container-header-content">
  108 + {{ dataGram.garOrderHandleName }}
  109 + </view>
103 110 </view>
104   - </view>
105   - <view class="order-detail-container-header-item">
106   - <text class="order-detail-container-header-title">装车照片:</text>
107   - <view class="order-detail-container-header-content">
108   - <u-upload :fileList="putOnImages" name="3" multiple :maxCount="20" :previewFullImage="true"
109   - :isReadOnly="true"></u-upload>
  111 + <view class="order-detail-container-header-item">
  112 + <text class="order-detail-container-header-title">联系方式:</text>
  113 + <view class="order-detail-container-header-content">
  114 + {{ dataGram.garOrderHandleTel }}
  115 + </view>
110 116 </view>
111   - </view>
112   - <view class="order-detail-container-header-item">
113   - <text class="order-detail-container-header-title">卸车照片:</text>
114   - <view class="order-detail-container-header-content">
115   - <u-upload :fileList="putDownImages" name="3" multiple :maxCount="20" :previewFullImage="true"
116   - :isReadOnly="true"></u-upload>
  117 + <view class="order-detail-container-header-item">
  118 + <text class="order-detail-container-header-title">装车照片:</text>
  119 + <view class="order-detail-container-header-content">
  120 + <u-upload :fileList="putOnImages" name="3" multiple :maxCount="20" :previewFullImage="true"
  121 + :isReadOnly="true"></u-upload>
  122 + </view>
117 123 </view>
118   - </view>
119   - <!-- <view class="order-detail-container-header-item">
120   - <text class="order-detail-container-header-title">备注:</text>
121   - <view class="order-detail-container-header-content">
122   - {{ dataGram.garRemark }}
  124 + <view class="order-detail-container-header-item">
  125 + <text class="order-detail-container-header-title">卸车照片:</text>
  126 + <view class="order-detail-container-header-content">
  127 + <u-upload :fileList="putDownImages" name="3" multiple :maxCount="20" :previewFullImage="true"
  128 + :isReadOnly="true"></u-upload>
  129 + </view>
123 130 </view>
124   - </view> -->
  131 + </view>
  132 + <view v-else class="empty-image"
  133 + style="width: 100%; display: flex; justify-content: center; align-items: center;">
  134 + <image class="image-style" style="width: 200rpx; height: 200rpx;" :src="emptyBase64Image"></image>
  135 + </view>
125 136 </view>
126 137 </view>
127   - <view class="space-box"></view>
  138 + <!-- 占位符 -->
  139 + <view class="space-box">{{ spaceStr }}</view>
128 140 <view class="order-detail-bottom">
129 141 <view class="order-detail-bottom-box">
130 142 <view class="order-detail-bottom-left">
... ... @@ -135,8 +147,8 @@
135 147 <view class="order-detail-bottom-right">
136 148 <u-button @click="handleOrder(orderId)" v-if="dataGram.garOrderHandlerStatus == 0 && dataGram.handleFlag"
137 149 shape="circle" color="#a9e08f" text="处理派单"></u-button>
138   - <u-button @click="handleUploadImage(orderId)" v-if="dataGram.garOrderHandlerStatus == 1" shape="circle"
139   - color="#a9e08f" text="上传照片"></u-button>
  150 + <u-button @click="handleUploadImage(orderId)" v-if="dataGram.garOrderHandlerStatus == 1 && dataGram.handleFlag"
  151 + shape="circle" color="#a9e08f" text="上传照片"></u-button>
140 152 <u-button @click="handleEvaluate(orderId)" v-if="dataGram.garEvaluateFlag == 0" shape="circle" color="#a9e08f"
141 153 text="去评价"></u-button>
142 154 </view>
... ... @@ -147,6 +159,7 @@
147 159  
148 160 <script setup>
149 161 import { queryOrderDetail, updateOrder } from "@/apis/order.js";
  162 +import zStatic from '@/components/z-paging/js/z-paging-static';
150 163 import { onLoad, onShow } from '@dcloudio/uni-app';
151 164 import { ref } from 'vue';
152 165 const dataGram = ref();
... ... @@ -154,11 +167,14 @@ const orderId = ref(null)
154 167 const currentImages = ref([])
155 168 const putOnImages = ref([])
156 169 const putDownImages = ref([])
  170 +const emptyBase64Image = ref(zStatic.base64Empty)
  171 +const spaceStr = ref("")
157 172 /**
158 173 * 初始化信息
159 174 */
160 175 onLoad((options) => {
161 176 orderId.value = options.orderId
  177 + console.log(emptyBase64Image.value);
162 178 })
163 179 const handleOrderDetail = (orderId) => {
164 180 queryOrderDetail(orderId).then(res => {
... ... @@ -209,7 +225,7 @@ const handleEvaluate = (orderId) =&gt; [
209 225 const handleOrder = (orderId) => {
210 226 updateOrder({ garOrderId: orderId, handleType: 0 }).then(res => {
211 227 if (res.data.success) {
212   - uni.$u.toast("派单状态已被改变")
  228 + uni.$u.toast(res.data.data)
213 229 handleOrderDetail(orderId)
214 230 }
215 231 })
... ... @@ -257,6 +273,7 @@ $custom-bottom-height: 200rpx;
257 273 background-color: $u-info-light;
258 274 box-sizing: border-box;
259 275 overflow-y: scroll;
  276 + background: linear-gradient(to bottom, $u-success-dark, $u-info-light, $u-info-light, $u-info-light);
260 277  
261 278  
262 279 .order-detail-container-box {
... ... @@ -264,7 +281,6 @@ $custom-bottom-height: 200rpx;
264 281 width: 100%;
265 282 padding: $custom-page-padding;
266 283 box-sizing: border-box;
267   - background: linear-gradient(to bottom, $u-success-dark, $u-info-light, $u-info-light, $u-info-light);
268 284  
269 285 .order-detail-top {
270 286 @include card();
... ... @@ -312,6 +328,7 @@ $custom-bottom-height: 200rpx;
312 328  
313 329 .space-box {
314 330 padding-bottom: $custom-bottom-height;
  331 + margin-bottom: 40rpx;
315 332 }
316 333  
317 334  
... ...
garbage-removal/src/pages/order/upload/index.vue
... ... @@ -98,12 +98,25 @@ const handleSubmit = (id, type) =&gt; {
98 98 }
99 99  
100 100 const validateImage = () => {
  101 +
101 102 for (let index = 0; index < fileList.value.length; index++) {
102 103 const str = fileList.value[index].url;
  104 + // #ifdef H5
103 105 if (str.startsWith("blob")) {
104 106 return false;
105 107 }
  108 + // #endif
  109 +
  110 + // #ifdef MP-WEIXIN
  111 + if (uni.$u.test.contains(str, "/tmp/")) {
  112 + return false;
  113 + }
  114 + // #endif
  115 +
106 116 }
  117 + //
  118 + // #ifdef
  119 +
107 120 return true;
108 121 }
109 122  
... ...
garbage-removal/src/pages/wode/index.vue
... ... @@ -18,42 +18,61 @@
18 18  
19 19 <view class="u-m-t-20">
20 20 <u-cell-group>
21   - <u-cell icon="map" title="地址管理"></u-cell>
  21 + <u-cell icon="map" @click="handleAddressManager" title="地址管理"></u-cell>
22 22 </u-cell-group>
23 23 </view>
24 24  
25 25 <view class="u-m-t-20">
26   - <u-cell-group>
  26 + <!-- <u-cell-group>
27 27 <u-cell icon="star" title="收藏"></u-cell>
28 28 <u-cell icon="photo" title="相册"></u-cell>
29 29 <u-cell icon="coupon" title="卡券"></u-cell>
30 30 <u-cell icon="heart" title="关注"></u-cell>
31   - </u-cell-group>
  31 + </u-cell-group> -->
32 32 </view>
33 33  
34   - <view class="u-m-t-20">
  34 + <view class="u-m-t-40">
35 35 <u-cell-group>
36   - <u-cell icon="setting" title="设置"></u-cell>
  36 + <u-cell icon="setting" @click="handleLoginOut" title="退出登录"></u-cell>
37 37 </u-cell-group>
38 38 </view>
39 39 </view>
40 40 </template>
41 41  
42   -<script>
  42 +<script setup>
  43 +import { loginOut } from "@/apis/user";
43 44 import headImg from "@/static/image/st_pic.png";
44   -export default {
45   - data() {
46   - return {
47   - pic: headImg,
48   - show: true
  45 +import { useMainStore } from "@/stores/index";
  46 +import { ref } from "vue";
  47 +const store = useMainStore();
  48 +const pic = ref(headImg)
  49 +const show = ref(true)
  50 +const handleAddressManager = () => {
  51 + uni.$u.route({
  52 + url: `pages/home/address/index`,
  53 + })
  54 +}
  55 +const handleLoginOut = () => {
  56 + uni.showModal({
  57 + title: '提示',
  58 + content: '确定要退出码',
  59 + success: function (res) {
  60 + if (res.confirm) {
  61 + loginOut().then(res => {
  62 + if (res.data.success) {
  63 + store.token = null;
  64 + uni.$u.toast("已退出")
  65 + uni.$u.route({
  66 + type: "reLaunch",
  67 + url: "pages/login/index"
  68 + })
  69 + }
  70 + })
  71 + } else if (res.cancel) {
  72 + console.log('用户点击取消');
  73 + }
49 74 }
50   - },
51   - onLoad() {
52   -
53   - },
54   - methods: {
55   -
56   - }
  75 + });
57 76 }
58 77 </script>
59 78  
... ...
garbage-removal/src/static/image/logo-1.png deleted 100644 → 0

3.93 KB

garbage-removal/src/static/image/logo.png deleted 100644 → 0

3.93 KB

garbage-removal/src/utils/request/request.js
... ... @@ -95,7 +95,7 @@ const reSetLoginStatus = () =&gt; {
95 95 title: "登录已过期,请重新登录!",
96 96 icon: "none",
97 97 });
98   - uni.navigateTo({
  98 + uni.reLaunch({
99 99 url: "/pages/login/index",
100 100 })
101 101 return
... ...
garbage-removal/src/uview-plus/components/u-message-input/u-message-input.vue
1 1 <template>
2 2 <view class="u-char-box">
3 3 <view class="u-char-flex">
4   - <input :disabled="disabledKeyboard" :value="valueModel" type="number" :focus="focus" :maxlength="maxlength" class="u-input" @input="getVal"/>
  4 + <input :disabled="disabledKeyboard" :value="valueModel" type="number" :focus="focus" :maxlength="maxlength"
  5 + class="u-input" @input="getVal" />
5 6 <view v-for="(item, index) in loopCharArr" :key="index">
6 7 <view :class="[breathe && charArrLength == index ? 'u-breathe' : '', 'u-char-item',
7 8 charArrLength === index && mode == 'box' ? 'u-box-active' : '',
8 9 mode === 'box' ? 'u-box' : '']" :style="{
9   - fontWeight: bold ? 'bold' : 'normal',
10   - fontSize: fontSize + 'rpx',
11   - width: width + 'rpx',
12   - height: width + 'rpx',
13   - color: inactiveColor,
14   - borderColor: charArrLength === index && mode == 'box' ? activeColor : inactiveColor
15   - }">
  10 + fontWeight: bold ? 'bold' : 'normal',
  11 + fontSize: fontSize + 'rpx',
  12 + width: width + 'rpx',
  13 + height: width + 'rpx',
  14 + color: inactiveColor,
  15 + borderColor: charArrLength === index && mode == 'box' ? activeColor : inactiveColor
  16 +}">
16 17 <view class="u-placeholder-line" :style="{
17   - display: charArrLength === index ? 'block' : 'none',
18   - height: width * 0.5 +'rpx'
19   - }"
20   - v-if="mode !== 'middleLine'"
21   - ></view>
22   - <view v-if="mode === 'middleLine' && charArrLength <= index" :class="[breathe && charArrLength == index ? 'u-breathe' : '', charArrLength === index ? 'u-middle-line-active' : '']"
23   - class="u-middle-line" :style="{height: bold ? '4px' : '2px', background: charArrLength === index ? activeColor : inactiveColor}"></view>
24   - <view v-if="mode === 'bottomLine'" :class="[breathe && charArrLength == index ? 'u-breathe' : '', charArrLength === index ? 'u-bottom-line-active' : '']"
25   - class="u-bottom-line" :style="{height: bold ? '4px' : '2px', background: charArrLength === index ? activeColor : inactiveColor}"></view>
26   - <block v-if="!dotFill"> {{ charArr[index] ? charArr[index] : ''}}</block>
  18 + display: charArrLength === index ? 'block' : 'none',
  19 + height: width * 0.5 + 'rpx'
  20 + }" v-if="mode !== 'middleLine'"></view>
  21 + <view v-if="mode === 'middleLine' && charArrLength <= index"
  22 + :class="[breathe && charArrLength == index ? 'u-breathe' : '', charArrLength === index ? 'u-middle-line-active' : '']"
  23 + class="u-middle-line"
  24 + :style="{ height: bold ? '4px' : '2px', background: charArrLength === index ? activeColor : inactiveColor }">
  25 + </view>
  26 + <view v-if="mode === 'bottomLine'"
  27 + :class="[breathe && charArrLength == index ? 'u-breathe' : '', charArrLength === index ? 'u-bottom-line-active' : '']"
  28 + class="u-bottom-line"
  29 + :style="{ height: bold ? '4px' : '2px', background: charArrLength === index ? activeColor : inactiveColor }">
  30 + </view>
  31 + <block v-if="!dotFill"> {{ charArr[index] ? charArr[index] : '' }}</block>
27 32 <block v-else>
28   - <text class="u-dot">{{ charArr[index] ? '●' : ''}}</text>
  33 + <text class="u-dot">{{ charArr[index] ? '●' : '' }}</text>
29 34 </block>
30 35 </view>
31 36 </view>
... ... @@ -34,285 +39,287 @@
34 39 </template>
35 40  
36 41 <script>
37   - /**
38   - * messageInput 验证码输入框
39   - * @description 该组件一般用于验证用户短信验证码的场景,也可以结合uView的键盘组件使用
40   - * @tutorial https://www.uviewui.com/components/messageInput.html
41   - * @property {String Number} maxlength 输入字符个数(默认4)
42   - * @property {Boolean} dot-fill 是否用圆点填充(默认false)
43   - * @property {String} mode 模式选择,见上方"基本使用"说明(默认box)
44   - * @property {String Number} value 预置值
45   - * @property {Boolean} breathe 是否开启呼吸效果,见上方说明(默认true)
46   - * @property {Boolean} focus 是否自动获取焦点(默认false)
47   - * @property {Boolean} bold 字体和输入横线是否加粗(默认true)
48   - * @property {String Number} font-size 字体大小,单位rpx(默认60)
49   - * @property {String} active-color 当前激活输入框的样式(默认#2979ff)
50   - * @property {String} inactive-color 非激活输入框的样式,文字颜色同此值(默认#606266)
51   - * @property {String | Number} width 输入框宽度,单位rpx,高等于宽(默认80)
52   - * @property {Boolean} disabled-keyboard 禁止点击输入框唤起系统键盘(默认false)
53   - * @event {Function} change 输入内容发生改变时触发,具体见官网说明
54   - * @event {Function} finish 输入字符个数达maxlength值时触发,见官网说明
55   - * @example <u-message-input mode="bottomLine"></u-message-input>
56   - */
57   - export default {
58   - name: "u-message-input",
59   - props: {
60   - // 最大输入长度
61   - maxlength: {
62   - type: [Number, String],
63   - default: 4
64   - },
65   - // 是否用圆点填充
66   - dotFill: {
67   - type: Boolean,
68   - default: false
69   - },
70   - // 显示模式,box-盒子模式,bottomLine-横线在底部模式,middleLine-横线在中部模式
71   - mode: {
72   - type: String,
73   - default: "box"
74   - },
75   - // 预置值
76   - modelValue: {
77   - type: [String, Number],
78   - default: ''
79   - },
80   - // 当前激活输入item,是否带有呼吸效果
81   - breathe: {
82   - type: Boolean,
83   - default: true
84   - },
85   - // 是否自动获取焦点
86   - focus: {
87   - type: Boolean,
88   - default: false
89   - },
90   - // 字体是否加粗
91   - bold: {
92   - type: Boolean,
93   - default: false
94   - },
95   - // 字体大小
96   - fontSize: {
97   - type: [String, Number],
98   - default: 60
99   - },
100   - // 激活样式
101   - activeColor: {
102   - type: String,
103   - default: '#2979ff'
104   - },
105   - // 未激活的样式
106   - inactiveColor: {
107   - type: String,
108   - default: '#606266'
109   - },
110   - // 输入框的大小,单位rpx,宽等于高
111   - width: {
112   - type: [Number, String],
113   - default: '80'
114   - },
115   - // 是否隐藏原生键盘,如果想用自定义键盘的话,需设置此参数为true
116   - disabledKeyboard: {
117   - type: Boolean,
118   - default: false
119   - }
  42 +/**
  43 + * messageInput 验证码输入框
  44 + * @description 该组件一般用于验证用户短信验证码的场景,也可以结合uView的键盘组件使用
  45 + * @tutorial https://www.uviewui.com/components/messageInput.html
  46 + * @property {String Number} maxlength 输入字符个数(默认4)
  47 + * @property {Boolean} dot-fill 是否用圆点填充(默认false)
  48 + * @property {String} mode 模式选择,见上方"基本使用"说明(默认box)
  49 + * @property {String Number} value 预置值
  50 + * @property {Boolean} breathe 是否开启呼吸效果,见上方说明(默认true)
  51 + * @property {Boolean} focus 是否自动获取焦点(默认false)
  52 + * @property {Boolean} bold 字体和输入横线是否加粗(默认true)
  53 + * @property {String Number} font-size 字体大小,单位rpx(默认60)
  54 + * @property {String} active-color 当前激活输入框的样式(默认#2979ff)
  55 + * @property {String} inactive-color 非激活输入框的样式,文字颜色同此值(默认#606266)
  56 + * @property {String | Number} width 输入框宽度,单位rpx,高等于宽(默认80)
  57 + * @property {Boolean} disabled-keyboard 禁止点击输入框唤起系统键盘(默认false)
  58 + * @event {Function} change 输入内容发生改变时触发,具体见官网说明
  59 + * @event {Function} finish 输入字符个数达maxlength值时触发,见官网说明
  60 + * @example <u-message-input mode="bottomLine"></u-message-input>
  61 + */
  62 +export default {
  63 + name: "u-message-input",
  64 + props: {
  65 + // 最大输入长度
  66 + maxlength: {
  67 + type: [Number, String],
  68 + default: 4
  69 + },
  70 + // 是否用圆点填充
  71 + dotFill: {
  72 + type: Boolean,
  73 + default: false
  74 + },
  75 + // 显示模式,box-盒子模式,bottomLine-横线在底部模式,middleLine-横线在中部模式
  76 + mode: {
  77 + type: String,
  78 + default: "box"
  79 + },
  80 + // 预置值
  81 + modelValue: {
  82 + type: [String, Number],
  83 + default: ''
  84 + },
  85 + // 当前激活输入item,是否带有呼吸效果
  86 + breathe: {
  87 + type: Boolean,
  88 + default: true
  89 + },
  90 + // 是否自动获取焦点
  91 + focus: {
  92 + type: Boolean,
  93 + default: false
  94 + },
  95 + // 字体是否加粗
  96 + bold: {
  97 + type: Boolean,
  98 + default: false
  99 + },
  100 + // 字体大小
  101 + fontSize: {
  102 + type: [String, Number],
  103 + default: 60
  104 + },
  105 + // 激活样式
  106 + activeColor: {
  107 + type: String,
  108 + default: '#2979ff'
120 109 },
121   - watch: {
122   - // maxlength: {
123   - // // 此值设置为true,会在组件加载后无需maxlength变化就会执行一次本监听函数,无需再created生命周期中处理
124   - // immediate: true,
125   - // handler(val) {
126   - // this.maxlength = Number(val);
127   - // }
128   - // },
129   - modelValue: {
130   - immediate: true,
131   - handler(val) {
132   - // 转为字符串
133   - val = String(val);
134   - // 超出部分截掉
135   - this.valueModel = val.substring(0, this.maxlength);
136   - }
137   - },
  110 + // 未激活的样式
  111 + inactiveColor: {
  112 + type: String,
  113 + default: '#606266'
138 114 },
139   - data() {
140   - return {
141   - valueModel: ""
  115 + // 输入框的大小,单位rpx,宽等于高
  116 + width: {
  117 + type: [Number, String],
  118 + default: '80'
  119 + },
  120 + // 是否隐藏原生键盘,如果想用自定义键盘的话,需设置此参数为true
  121 + disabledKeyboard: {
  122 + type: Boolean,
  123 + default: false
  124 + }
  125 + },
  126 + watch: {
  127 + // maxlength: {
  128 + // // 此值设置为true,会在组件加载后无需maxlength变化就会执行一次本监听函数,无需再created生命周期中处理
  129 + // immediate: true,
  130 + // handler(val) {
  131 + // this.maxlength = Number(val);
  132 + // }
  133 + // },
  134 + modelValue: {
  135 + immediate: true,
  136 + handler(val) {
  137 + // 转为字符串
  138 + val = String(val);
  139 + // 超出部分截掉
  140 + this.valueModel = val.substring(0, this.maxlength);
142 141 }
143 142 },
144   - emits: ['change', 'finish'],
145   - computed: {
146   - // 是否显示呼吸灯效果
147   - animationClass() {
148   - return (index) => {
149   - if (this.breathe && this.charArr.length == index) return 'u-breathe';
150   - else return '';
151   - }
152   - },
153   - // 用于显示字符
154   - charArr() {
155   - return this.valueModel.split('');
156   - },
157   - charArrLength() {
158   - return this.charArr.length;
159   - },
160   - // 根据长度,循环输入框的个数,因为头条小程序数值不能用于v-for
161   - loopCharArr() {
162   - return new Array(this.maxlength);
  143 + },
  144 + data() {
  145 + return {
  146 + valueModel: ""
  147 + }
  148 + },
  149 + emits: ['change', 'finish'],
  150 + computed: {
  151 + // 是否显示呼吸灯效果
  152 + animationClass() {
  153 + return (index) => {
  154 + if (this.breathe && this.charArr.length == index) return 'u-breathe';
  155 + else return '';
163 156 }
164 157 },
165   - methods: {
166   - getVal(e) {
167   - let {
168   - value
169   - } = e.detail
170   - this.valueModel = value;
171   - // 判断长度是否超出了maxlength值,理论上不会发生,因为input组件设置了maxlength属性值
172   - if (String(value).length > this.maxlength) return;
173   - // 未达到maxlength之前,发送change事件,达到后发送finish事件
174   - this.$emit('change', value);
175   - if (String(value).length == this.maxlength) {
176   - this.$emit('finish', value);
177   - }
  158 + // 用于显示字符
  159 + charArr() {
  160 + return this.valueModel.split('');
  161 + },
  162 + charArrLength() {
  163 + return this.charArr.length;
  164 + },
  165 + // 根据长度,循环输入框的个数,因为头条小程序数值不能用于v-for
  166 + loopCharArr() {
  167 + return new Array(this.maxlength);
  168 + }
  169 + },
  170 + methods: {
  171 + getVal(e) {
  172 + let {
  173 + value
  174 + } = e.detail
  175 + this.valueModel = value;
  176 + // 判断长度是否超出了maxlength值,理论上不会发生,因为input组件设置了maxlength属性值
  177 + if (String(value).length > this.maxlength) return;
  178 + // 未达到maxlength之前,发送change事件,达到后发送finish事件
  179 + this.$emit('change', value);
  180 + if (String(value).length == this.maxlength) {
  181 + this.$emit('finish', value);
178 182 }
179 183 }
180 184 }
  185 +}
181 186 </script>
182 187  
183 188 <style scoped lang="scss">
184   - // 定义混入指令,用于在非nvue环境下的flex定义,因为nvue没有display属性,会报错
185   - @mixin vue-flex($direction: row) {
186   - /* #ifndef APP-NVUE */
187   - display: flex;
188   - flex-direction: $direction;
189   - /* #endif */
190   - }
191   -
192   - @keyframes breathe {
193   - 0% {
194   - opacity: 0.3;
195   - }
  189 +// 定义混入指令,用于在非nvue环境下的flex定义,因为nvue没有display属性,会报错
  190 +@mixin vue-flex($direction: row) {
  191 + /* #ifndef APP-NVUE */
  192 + display: flex;
  193 + flex-direction: $direction;
  194 + /* #endif */
  195 +}
196 196  
197   - 50% {
198   - opacity: 1;
199   - }
200   -
201   - 100% {
202   - opacity: 0.3;
203   - }
  197 +@keyframes breathe {
  198 + 0% {
  199 + opacity: 0.3;
204 200 }
205 201  
206   - .u-char-box {
207   - text-align: center;
  202 + 50% {
  203 + opacity: 1;
208 204 }
209 205  
210   - .u-char-flex {
211   - @include vue-flex;
212   - justify-content: center;
213   - flex-wrap: wrap;
214   - position: relative;
  206 + 100% {
  207 + opacity: 0.3;
215 208 }
  209 +}
216 210  
217   - .u-input {
218   - position: absolute;
219   - top: 0;
220   - left: -100%;
221   - width: 200%;
222   - height: 100%;
223   - text-align: left;
224   - z-index: 9;
225   - opacity: 0;
226   - background: none;
227   - }
  211 +.u-char-box {
  212 + text-align: center;
  213 +}
228 214  
229   - .u-char-item {
230   - position: relative;
231   - width: 90rpx;
232   - height: 90rpx;
233   - margin: 10rpx 10rpx;
234   - font-size: 60rpx;
235   - font-weight: bold;
236   - color: $u-main-color;
237   - line-height: 90rpx;
238   - @include vue-flex;
239   - justify-content: center;
240   - align-items: center;
241   - }
  215 +.u-char-flex {
  216 + @include vue-flex;
  217 + justify-content: center;
  218 + flex-wrap: wrap;
  219 + position: relative;
  220 + -webkit-user-select: text !important;
  221 +}
242 222  
243   - .u-middle-line {
244   - border: none;
245   - }
  223 +.u-input {
  224 + position: absolute;
  225 + top: 0;
  226 + left: -100%;
  227 + width: 200%;
  228 + height: 100%;
  229 + text-align: left;
  230 + z-index: 9;
  231 + opacity: 0;
  232 + background: none;
  233 + -webkit-user-select: text !important;
  234 +}
246 235  
247   - .u-box {
248   - box-sizing: border-box;
249   - border: 2rpx solid #cccccc;
250   - border-radius: 6rpx;
251   - }
  236 +.u-char-item {
  237 + position: relative;
  238 + width: 90rpx;
  239 + height: 90rpx;
  240 + margin: 10rpx 10rpx;
  241 + font-size: 60rpx;
  242 + font-weight: bold;
  243 + color: $u-main-color;
  244 + line-height: 90rpx;
  245 + @include vue-flex;
  246 + justify-content: center;
  247 + align-items: center;
  248 +}
252 249  
253   - .u-box-active {
254   - overflow: hidden;
255   - animation-timing-function: ease-in-out;
256   - animation-duration: 1500ms;
257   - animation-iteration-count: infinite;
258   - animation-direction: alternate;
259   - border: 2rpx solid $u-primary;
260   - }
  250 +.u-middle-line {
  251 + border: none;
  252 +}
261 253  
262   - .u-middle-line-active {
263   - background: $u-primary;
264   - }
  254 +.u-box {
  255 + box-sizing: border-box;
  256 + border: 2rpx solid #cccccc;
  257 + border-radius: 6rpx;
  258 +}
265 259  
266   - .u-breathe {
267   - animation: breathe 2s infinite ease;
268   - }
  260 +.u-box-active {
  261 + overflow: hidden;
  262 + animation-timing-function: ease-in-out;
  263 + animation-duration: 1500ms;
  264 + animation-iteration-count: infinite;
  265 + animation-direction: alternate;
  266 + border: 2rpx solid $u-primary;
  267 +}
269 268  
270   - .u-placeholder-line {
271   - /* #ifndef APP-NVUE */
272   - display: none;
273   - /* #endif */
274   - position: absolute;
275   - left: 50%;
276   - top: 50%;
277   - transform: translate(-50%, -50%);
278   - width: 2rpx;
279   - height: 40rpx;
280   - background: #333333;
281   - animation: twinkling 1.5s infinite ease;
282   - }
  269 +.u-middle-line-active {
  270 + background: $u-primary;
  271 +}
283 272  
284   - .u-animation-breathe {
285   - animation-name: breathe;
286   - }
  273 +.u-breathe {
  274 + animation: breathe 2s infinite ease;
  275 +}
287 276  
288   - .u-dot {
289   - font-size: 34rpx;
290   - line-height: 34rpx;
291   - }
  277 +.u-placeholder-line {
  278 + /* #ifndef APP-NVUE */
  279 + display: none;
  280 + /* #endif */
  281 + position: absolute;
  282 + left: 50%;
  283 + top: 50%;
  284 + transform: translate(-50%, -50%);
  285 + width: 2rpx;
  286 + height: 40rpx;
  287 + background: #333333;
  288 + animation: twinkling 1.5s infinite ease;
  289 +}
292 290  
293   - .u-middle-line {
294   - height: 4px;
295   - background: #000000;
296   - width: 80%;
297   - position: absolute;
298   - border-radius: 2px;
299   - top: 50%;
300   - left: 50%;
301   - transform: translate(-50%, -50%);
302   - }
  291 +.u-animation-breathe {
  292 + animation-name: breathe;
  293 +}
303 294  
304   - .u-bottom-line-active {
305   - background: $u-primary;
306   - }
  295 +.u-dot {
  296 + font-size: 34rpx;
  297 + line-height: 34rpx;
  298 +}
307 299  
308   - .u-bottom-line {
309   - height: 4px;
310   - background: #000000;
311   - width: 80%;
312   - position: absolute;
313   - border-radius: 2px;
314   - bottom: 0;
315   - left: 50%;
316   - transform: translate(-50%);
317   - }
  300 +.u-middle-line {
  301 + height: 4px;
  302 + background: #000000;
  303 + width: 80%;
  304 + position: absolute;
  305 + border-radius: 2px;
  306 + top: 50%;
  307 + left: 50%;
  308 + transform: translate(-50%, -50%);
  309 +}
  310 +
  311 +.u-bottom-line-active {
  312 + background: $u-primary;
  313 +}
  314 +
  315 +.u-bottom-line {
  316 + height: 4px;
  317 + background: #000000;
  318 + width: 80%;
  319 + position: absolute;
  320 + border-radius: 2px;
  321 + bottom: 0;
  322 + left: 50%;
  323 + transform: translate(-50%);
  324 +}
318 325 </style>
... ...