فهرست منبع

修改不同的页面的请求头

lisy 1 ماه پیش
والد
کامیت
9f1ee6fa95
53فایلهای تغییر یافته به همراه1112 افزوده شده و 3199 حذف شده
  1. 3 2
      .env.development
  2. 3 2
      .env.production
  3. 236 203
      pnpm-lock.yaml
  4. 12 12
      src/App.vue
  5. 278 0
      src/assets/styles/common.scss
  6. 141 0
      src/assets/styles/public.scss
  7. 3 0
      src/assets/styles/theme.scss
  8. 40 0
      src/assets/styles/uview-plus.scss
  9. 0 67
      src/components/ut-ad-item/ut-ad-item.vue
  10. 0 121
      src/components/ut-ad-swiper/ut-ad-swiper.vue
  11. 0 62
      src/components/ut-album/ut-album.vue
  12. 0 54
      src/components/ut-avater-icon/ut-avater-icon.vue
  13. 0 95
      src/components/ut-base-model/ut-base-model.vue
  14. 0 23
      src/components/ut-button-add/ut-button-add.vue
  15. 0 154
      src/components/ut-checkbox-cln/ut-checkbox-cln.vue
  16. 0 53
      src/components/ut-datetime-picker/ut-datetime-picker.vue
  17. 0 32
      src/components/ut-empty/ut-empty.vue
  18. 0 64
      src/components/ut-file/ut-file.vue
  19. 0 139
      src/components/ut-files-upload/ut-files-upload.vue
  20. 0 48
      src/components/ut-image/ut-image.vue
  21. 0 176
      src/components/ut-index-list/ut-index-list.vue
  22. 0 27
      src/components/ut-loading-view/ut-loading-view.vue
  23. 0 66
      src/components/ut-marquee-animation/ut-marquee-animation.vue
  24. 0 98
      src/components/ut-navbar-model/ut-navbar-model.vue
  25. 0 10
      src/components/ut-navbar/ut-navbar.vue
  26. 0 63
      src/components/ut-picker-date/ut-picker-date.vue
  27. 0 73
      src/components/ut-picker-region/ut-picker-region.vue
  28. 0 173
      src/components/ut-picker/ut-picker.vue
  29. 0 198
      src/components/ut-pickup-date/ut-pickup-date.vue
  30. 0 99
      src/components/ut-search/ut-search.vue
  31. 0 34
      src/components/ut-select-down/ut-select-down.vue
  32. 0 122
      src/components/ut-tabar/ut-tabar.vue
  33. 0 68
      src/components/ut-tabs-card/ut-tabs-card.vue
  34. 0 114
      src/components/ut-tabs-dict/ut-tabs-dict.vue
  35. 0 87
      src/components/ut-tabs-items/ut-tabs-items.vue
  36. 0 55
      src/components/ut-tabs-pages/ut-tabs-pages.vue
  37. 0 77
      src/components/ut-tabs-xpf/ut-tabs-xpf.vue
  38. 0 68
      src/components/ut-tabs/ut-tabs.vue
  39. 0 36
      src/components/ut-tag-dict/ut-tag-dict.vue
  40. 0 44
      src/components/ut-title/ut-title.vue
  41. 0 99
      src/components/ut-upload-image/ut-upload-image.vue
  42. 0 139
      src/components/ut-upload/ut-upload.vue
  43. 4 2
      src/main.ts
  44. 80 38
      src/pages.json
  45. 27 35
      src/pages/index/index.vue
  46. 14 0
      src/pages/plant/index.vue
  47. 12 0
      src/pages/production/index.vue
  48. 10 0
      src/plant/index/index.vue
  49. 10 0
      src/production/index/index.vue
  50. 3 1
      src/uni.scss
  51. 89 60
      src/utils/request.ts
  52. 0 0
      stats.html
  53. 147 6
      unocss.config.js

+ 3 - 2
.env.development

@@ -1,7 +1,8 @@
 # 页面标题
-VITE_TITLE = 中药材追溯H5
+VITE_TITLE = 林草平台
 
 # 开发环境配置
 VITE_APP_ENV = 'development'
 # api请求地址
-VITE_API_BASE_URL = 'https://www.shuziyunyao.com/dm-api'
+VITE_API_BASE_URL = 'https://www.shuziyunyao.com/dm-api'
+VITE_API_OTHER_BASE_URL = 'https://www.shuziyunyao.com/trace-api'

+ 3 - 2
.env.production

@@ -1,7 +1,8 @@
 # 页面标题
-VITE_APP_TITLE = 中药材追溯H5
+VITE_APP_TITLE = 林草平台
 
 # 开发环境配置
 VITE_APP_ENV = 'production'
 # api请求地址
-VITE_API_BASE_URL = 'https://www.shuziyunyao.com/dm-api'
+VITE_API_BASE_URL = 'https://www.shuziyunyao.com/dm-api'
+VITE_API_OTHER_BASE_URL = 'https://www.shuziyunyao.com/trace-api'

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 236 - 203
pnpm-lock.yaml


+ 12 - 12
src/App.vue

@@ -1,25 +1,25 @@
 <script setup lang="ts">
-import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
+import { onLaunch, onShow, onHide } from '@dcloudio/uni-app';
 import { useAuthStore } from '@/store/modules/auth';
 import { autoLogin } from '@/utils/routeGuard';
 
 onLaunch(async () => {
-  console.log("App Launch");
-  
-  // 应用启动时检查登录状态
-  try {
-    await autoLogin();
-  } catch (error) {
-    console.error('Auto login check failed:', error);
-  }
+    console.log('App Launch');
+
+    // 应用启动时检查登录状态
+    try {
+        await autoLogin();
+    } catch (error) {
+        console.error('Auto login check failed:', error);
+    }
 });
 
 onShow(() => {
-  console.log("App Show");
+    console.log('App Show');
 });
 
 onHide(() => {
-  console.log("App Hide");
+    console.log('App Hide');
 });
 </script>
-<style></style>
+<style lang="scss"></style>

+ 278 - 0
src/assets/styles/common.scss

@@ -0,0 +1,278 @@
+.d-block {
+    display: block;
+}
+.navbar-logo {
+    width: 50rpx;
+    height: 50rpx;
+}
+
+.small-icon {
+    width: 38rpx;
+    height: 38rpx;
+}
+.base-icon {
+    width: 48rpx;
+    height: 48rpx;
+}
+.icon-36 {
+    width: 36rpx;
+    height: 36rpx;
+}
+.minn-icon {
+    width: 34rpx;
+    height: 34rpx;
+}
+.min-icon {
+    width: 30rpx;
+    height: 30rpx;
+}
+.small-icon-40 {
+    width: 40rpx;
+    height: 40rpx;
+}
+.active-hover {
+    opacity: 0.8;
+    cursor: pointer;
+}
+.base-page-banner {
+    position: absolute;
+    left: 0;
+    top: 0;
+    r: 0;
+    width: 100%;
+    z-index: -1;
+}
+.no-data-img {
+    width: 300rpx;
+    height: 300rpx;
+}
+.base-content {
+    padding: 24rpx;
+}
+.row-desc {
+    padding: 10rpx;
+    .row-lebal {
+        font-size: 30rpx;
+        color: #333;
+    }
+    .row-value {
+        padding: 30rpx 10rpx;
+        font-size: 30rpx;
+        color: #999;
+    }
+}
+
+.base-block {
+    border-radius: 10rpx;
+    background-color: #fff;
+}
+
+.b-r-c {
+    border-radius: 50%;
+}
+.b-radius {
+    border-radius: 16rpx;
+}
+.wl_bg {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    width: 43rpx;
+    height: 87rpx;
+    margin: auto;
+    z-index: 2;
+}
+.radius-10 {
+    border-radius: 10rpx;
+}
+
+// 移动端底部安全区域
+.safe-area {
+    padding-bottom: constant(safe-area-inset-bottom);
+    padding-bottom: env(safe-area-inset-bottom);
+}
+
+.base-lh {
+    line-height: 1.6;
+}
+
+.u-primary-border {
+    border: 1rpx solid rgba($u-primary, 0.1);
+}
+// 数字字母从中间换行
+.break-word {
+    word-break: break-all;
+}
+
+.base-btm-wrap {
+    position: relative;
+    box-shadow: 6rpx 0px 27rpx 0px rgba($u-primary, 0.3);
+}
+
+.vip-content {
+    min-height: 100rpx;
+    // opacity: .1;
+}
+// 首行缩进
+.base-lh {
+    text-indent: 2em;
+}
+.home_icon {
+    width: 46rpx;
+    height: 46rpx;
+}
+// 超链接
+.base-underline {
+    text-decoration: underline;
+}
+// 底部样式
+.base-bottom-wrap {
+    position: relative;
+    box-shadow: 6rpx 0px 27rpx 0px rgba(#2a6d52, 0.3);
+    background-color: #fff;
+}
+
+.scroll-x-info {
+    white-space: nowrap;
+}
+.base-tag {
+    padding: 5rpx 12rpx;
+    border-radius: 6rpx;
+}
+.border-bottom-f5 {
+    border-bottom: 1px solid #f5f5f5;
+}
+.border-bottom-f2 {
+    border-bottom: 1px solid #f2f2f2;
+}
+.btm-shadow {
+    box-shadow: 5rpx 0px 13rpx 0px #adadad;
+}
+.pop-msg-box {
+    color: #ff6a07;
+    background-color: #fdf8e2;
+    border-radius: 8rpx;
+}
+.up-border {
+    transform: rotate(360deg);
+}
+
+.badge-num_base {
+    position: absolute;
+    left: 50%;
+    top: 6rpx;
+    transform: translateX(2rpx);
+}
+.fab-drop-shadow {
+    width: 132rpx;
+    height: 132rpx;
+}
+.flex-nowrap {
+    flex-wrap: nowrap;
+}
+.flex-shrink-0 {
+    flex-shrink: 0;
+}
+.left-icon-bg {
+    background-color: rgba(0, 0, 0, 0.3);
+    padding: 10rpx;
+    border-radius: 4rpx;
+}
+.dot-wrap2 {
+  position: absolute;
+  right: 10rpx;
+  bottom: 10rpx;
+  padding: 6rpx 10rpx;
+  background-color: rgba(0, 0, 0, 0.3);
+  border-radius: 4rpx;
+  color: #fff;
+  font-size: 26rpx;
+}
+.money-tag {
+  padding: 10rpx 20rpx;
+  font-size: 30rpx;
+  color: #fff;
+  line-height: 1;
+  background-color:#F4403B;
+  border-radius: 40rpx;
+}
+.ut-tag-primary {
+  padding: 2rpx 10rpx;
+  border-radius: 8rpx;
+  color: #fff;
+  font-size: 26rpx;
+  background-color: $u-primary;
+}
+.ut-tag-danger-plain {
+  padding: 2rpx 10rpx;
+  border-radius: 8rpx;
+  color: #F4403B;
+  font-size: 26rpx;
+  border: 1rpx solid #F4C6C4;
+}
+.ut-tag-info {
+  padding: 2rpx 10rpx;
+  border-radius: 8rpx;
+  color: #333;
+  font-size: 26rpx;
+  background-color: #F2F2F2;
+}
+.ut-tag-danger-plain-min {
+  padding: 2rpx 10rpx;
+  border-radius: 4rpx;
+  color: #F4403B;
+  font-size: 22rpx;
+  border: 1rpx solid #F4C6C4;
+  transform: rotate(360deg);
+}
+
+.btn-two-primary {
+    width: 500rpx;
+    height: 90rpx;
+    border-radius: 10rpx;
+    overflow: hidden;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 36rpx;
+
+    .btn1 {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        flex: 1;
+        height: 100%;
+        color: #2A6D52;
+        background-color: #D4E2DC;
+    }
+    .btn2 {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        flex: 1;
+        height: 100%;
+        color: #FBE6A9;
+        background-color: #2A6D52;
+    }
+}
+.w-s-no{
+    white-space: nowrap;
+}
+// 超出两行省略
+.text-ellipsis-2 {
+    display: -webkit-box;
+    -webkit-box-orient: vertical;
+    -webkit-line-clamp: 2;
+    overflow: hidden;
+}
+
+.btm-wrap-shadow {
+    position: relative;
+    box-shadow: 6rpx 0px 27rpx 0px rgba(#2A6D52, 0.3);
+}
+.outview {
+    width: 100%;
+    max-width: 1000rpx;
+    height: 100vh;
+}

+ 141 - 0
src/assets/styles/public.scss

@@ -0,0 +1,141 @@
+@use "sass:math";
+// * {
+// 	box-sizing: border-box;
+// }
+.d-i-b {
+    display: inline-block;
+}
+.d-flex {
+    display: flex;
+}
+.f-w-w {
+    flex-wrap: wrap;
+}
+.a-c {
+    align-items: center;
+}
+.j-c {
+    justify-content: center;
+}
+.j-ed {
+    justify-content: flex-end;
+}
+.a-ed {
+    align-items: flex-end;
+}
+.flex-cln {
+    flex-direction: column;
+}
+.j-sb {
+    justify-content: space-between;
+}
+.flex-w {
+    flex-wrap: wrap;
+}
+.flex-at {
+    flex: auto;
+}
+.flex1 {
+    flex: 1;
+}
+.flex2 {
+    flex: 2;
+}
+.p-rtv {
+    position: relative;
+}
+
+// 斜体字
+.f-s-i {
+    font-style: italic;
+}
+// 文字靠右
+.text-right {
+    text-align: right;
+}
+.word-all {
+    word-break: break-all;
+}
+.ov-hd {
+    overflow: hidden;
+}
+.text-center {
+    text-align: center;
+}
+// 文字中间斜杠画去
+.text-through {
+    text-decoration: line-through;
+}
+// 文字换行
+.text-wrap {
+    white-space: normal;
+}
+.mg-at {
+    margin: auto;
+}
+.b-s-b-b {
+    box-sizing: border-box;
+}
+.over-auto {
+    overflow-y: auto;
+}
+
+$colors: (
+    000: #000,
+    333: #333,
+    666: #666,
+    999: #999,
+    fff: #fff,
+    ccc: #ccc,
+    eee: #eee,
+    ddd: #ddd,
+    fa: #fafafa,
+    gl: #affec2,
+    // 主题色
+    primary: #2A6D52,
+    light-black: #4d4d4d,
+    // 成功
+    success: #67c23a,
+    // 警告
+    warning: #e6a23c,
+    // 失败
+    danger: #f56c6c,
+    // 钱红色
+    money-red: #f4403b,
+    // 会员绿
+    member-green: #41c06d,
+    res-0: #f6881c
+);
+@each $color, $value in $colors {
+    .c-#{$color} {
+        color: $value;
+    }
+}
+@each $color, $value in $colors {
+    .bg-#{$color} {
+        background-color: $value;
+    }
+}
+:root {
+    --primary: #009932;
+    // 橙色
+    --orange: #ffa500;
+}
+// 首行缩进2个字符
+.text-indent-2 {
+    text-indent: 2em;
+}
+.text-desc {
+	padding: 12rpx;
+
+	// 偶数行背景色
+	&:nth-child(2n) {
+		background-color: #FAFAFA;
+	}
+}
+.btn-hover {
+    background-color: #f5f5f5;
+}
+.tx-ov{
+    text-overflow: ellipsis;
+}

+ 3 - 0
src/assets/styles/theme.scss

@@ -0,0 +1,3 @@
+.c-primary {
+    color: #67c23a !important;
+}

+ 40 - 0
src/assets/styles/uview-plus.scss

@@ -0,0 +1,40 @@
+.u-button--disabled.u-button.u-button--primary {
+    background-color: #ccc;
+    color: #999;
+    border-color: #ccc;
+    opacity: 1;
+}
+.base-min-btn {
+    &.u-button--mini.u-button {
+        font-size: 24rpx;
+        width: auto !important;
+        height: 44rpx !important;
+        min-width: 70rpx !important;
+        padding: 0 10rpx !important;
+    }
+}
+
+.u-navbar__content  .u-navbar__content__title.u-line-1 {
+    font-size: 38rpx;
+    font-weight: 500;
+}
+.fill-tabs .u-tabs__wrapper__nav {
+    .u-tabs__wrapper__nav__item {
+        padding: 0 10rpx !important;
+    }
+}
+.u-input__content__field-wrapper__field {
+    font-weight: 500;
+}
+.u-textarea__field {
+    color: #333 !important;
+    font-size: 30rpx !important;
+    font-weight: 500;
+}
+.input-placeholder {
+    color: #ccc;
+    font-weight: 400;
+}
+uni-button:after{
+    border: none;
+}

+ 0 - 67
src/components/ut-ad-item/ut-ad-item.vue

@@ -1,67 +0,0 @@
-<template>
-    <!-- 轮播图 -->
-    <view class="swiper-item" @click.stop="adRoute">
-        <image class="swiper-img" :src="item?.adImg" mode="heightFix" />
-    </view>
-</template>
-<script setup lang="ts" name="ad-item">
-// 移除了不必要的ref导入
-
-interface AdItem {
-    adImg: string;
-    adType: string;
-    adUrl: string;
-}
-
-interface Props {
-    item: AdItem;
-    index: number;
-    count: number;
-}
-
-const props = withDefaults(defineProps<Props>(), {
-    item: () => ({} as AdItem),
-    index: 0,
-    count: 1
-});
-
-const adRoute = () => {
-    if (props.item?.adType == '1') {
-        uni.$u.route({
-            type: 'navigateTo',
-            url: props.item?.adUrl,
-            params: {
-                ad: 1
-            }
-        });
-    }
-    if (props.item?.adType == '2') {
-        uni.$u.route({
-            type: 'navigateTo',
-            url: '/pages/tool/webview/index',
-            params: {
-                url: props.item?.adUrl
-            }
-        });
-    }
-};
-</script>
-<style lang="scss" scoped>
-.swiper-item {
-    position: absolute;
-    left: 0;
-    top: 0;
-    right: 0;
-    bottom: 0;
-    height: 100%;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    overflow: hidden;
-}
-
-.swiper-img {
-    width: 100%;
-    height: 100%;
-}
-</style>

+ 0 - 121
src/components/ut-ad-swiper/ut-ad-swiper.vue

@@ -1,121 +0,0 @@
-<template>
-    <view v-if="style === '1' && list.length" :class="{ 'up-border-bottom': borderBottom }" :style="cStyle">
-        <slot name="title"></slot>
-        <view class="p-rtv">
-            <swiper class="ad-swiper" :style="{ height }" @change="changeIndex" :display-multiple-items="count" autoplay circular
-                :indicator-dots="false">
-                <swiper-item v-for="(item, index) in list" :key="index">
-                    <ut-ad-item :item="item" :index="index" :count="count"></ut-ad-item>
-                </swiper-item>
-            </swiper>
-            <view v-if="isDot"  class="dot-wrap">
-                <view v-for="(item, index) in list" :key="index" class="dot-item" :class="{ checked: current === index }"></view>
-            </view>
-        </view>
-    </view>
-</template>
-<script setup lang="ts" name="ut-ad-swiper">
-import { useClientRequest } from '@/utils/request';
-import { getCurrentPage } from '@/utils/public';
-
-interface AdItem {
-    adImg: string;
-    adType: string;
-    adUrl: string;
-    id: string;
-}
-
-interface Props {
-    adPage: string;
-    style: string;
-    adPosition: string;
-    height: string;
-    margin: string;
-    padding: string;
-    count: number;
-    borderBottom: boolean;
-    cStyle: Record<string, any>;
-    pageSize: number;
-    isDot: boolean;
-}
-
-const props = withDefaults(defineProps<Props>(), {
-    adPage: '',
-    style: '1',
-    adPosition: '1',
-    height: '200rpx',
-    margin: '0',
-    padding: '0',
-    count: 1,
-    borderBottom: false,
-    cStyle: () => ({}),
-    pageSize: 6,
-    isDot: false
-});
-
-const current = ref(0);
-const changeIndex = (e: { detail: { current: number } }) => {
-    current.value = e.detail.current;
-};
-
-const list = ref<AdItem[]>([]);
-const params = ref({
-    pageNum: 1,
-    pageSize: 6,
-    adPage: props?.adPage || getCurrentPage().route,
-    status: 1,
-    stype: props.style,
-    adPosition: props.adPosition
-});
-
-const getList = async () => {
-    try {
-        const res: any = await useClientRequest.get('/api/ads', { params: params.value });
-        if (res?.code !== 200) return;
-        list.value = res.rows;
-    } catch (error) {
-        console.error('Failed to fetch ads:', error);
-    }
-};
-
-const refresh = () => {
-    getList();
-};
-
-defineExpose({
-    refresh
-});
-
-onMounted(() => {
-    getList();
-});
-</script>
-<style lang="scss" scoped>
-.ad-swiper {
-    height: 200rpx;
-}
-.dot-wrap {
-    position: absolute;
-    bottom: 10rpx;
-    left: 0;
-    z-index: 10;
-    right: 0;
-    height: 6rpx;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-
-    .dot-item {
-        width: 6rpx;
-        height: 6rpx;
-        border-radius: 3rpx;
-        background-color: #ccc;
-        margin: 0 4rpx;
-
-        &.checked {
-            width: 30rpx;
-            transition: all .2s;
-        }
-    }
-}
-</style>

+ 0 - 62
src/components/ut-album/ut-album.vue

@@ -1,62 +0,0 @@
-<template>
-    <template v-if="urls.length === 1">
-        <view class="ut-album-single" :style="{ width: singleSize, height: singleSize }" @click="previewImg(keyName ? urls[0][keyName] : urls[0])">
-            <image :style="{ width: singleSize, height: singleSize }" :src="keyName ? urls[0][keyName] : urls[0]" mode="aspectFill"></image>
-        </view>
-    </template>
-    <template v-else>
-        <up-album :urls="urls" :singleMode="singleMode" :space="space" :rowCount="rowCount" :previewFullImage="previewFullImage" :keyName="keyName"
-            :unit="unit" :singleSize="singleSize" :multipleSize="multipleSize">
-        </up-album>
-    </template>
-</template>
-<script setup>
-import {
-    ref
-} from 'vue';
-const props = defineProps({
-    urls: {
-        type: Array,
-        default: []
-    },
-    singleMode: {
-        type: String,
-        default: 'aspectFill'
-    },
-    space: {
-        type: String,
-        default: '14rpx'
-    },
-    rowCount: {
-        type: Number,
-        default: 4
-    },
-    keyName: {
-        type: String,
-        default: ''
-    },
-    unit: {
-        type: String,
-        default: 'rpx'
-    },
-    singleSize: {
-        type: String,
-        default: '160rpx'
-    },
-    multipleSize: {
-        type: String,
-        default: '160rpx'
-    },
-    previewFullImage: {
-        type: Boolean,
-        default: true
-    }
-})
-const previewImg = (url) => {
-	if (url && props.previewFullImage) {
-		uni.previewImage({
-		    urls: [url]
-		});
-	}
-};
-</script>

+ 0 - 54
src/components/ut-avater-icon/ut-avater-icon.vue

@@ -1,54 +0,0 @@
-<template>
-    <view v-if="isEdit" @click.stop="emit('click')" class="p-rtv" :style="{ background: bgColor, border: border ? '1px solid #F8F8F8' : 'none' }">
-        <up-avatar :size="size" :src="src || defaultSrc" :shape="shape"></up-avatar>
-        <image v-if="isEdit" class="edit-icon-box" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/card/edit_icon.png" mode="aspectFit" />
-    </view>
-    <view v-else  class="p-rtv">
-        <up-avatar :size="size" :src="src || defaultSrc" :shape="shape"></up-avatar>
-    </view>
-</template>
-<script setup>
-const props = defineProps({
-    src: {
-        type: String,
-        default: ''
-    },
-    size: {
-        type: String,
-        default: '116rpx'
-    },
-    defaultSrc: {
-        type: String,
-        default: 'https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/common/avatar.png'
-    },
-    shape: {
-        type: String,
-        default: 'square' // 'circle' or 'square'
-    },
-    border: {
-        type: Boolean,
-        default: false
-    },
-    // 背景颜色
-    bgColor: {
-        type: String,
-        default: '#EDEDED'
-    },
-    isEdit: {
-        type: Boolean,
-        default: true
-    }
-});
-const emit = defineEmits(['click']);
-</script>
-<style lang="scss" scoped>
-.edit-icon-box {
-    position: absolute;
-    right: 0rpx;
-    bottom: 0rpx;
-    width: 32rpx;
-    height: 32rpx;
-    background-color: #2A6D52;
-    border-radius: 50%;
-}
-</style>

+ 0 - 95
src/components/ut-base-model/ut-base-model.vue

@@ -1,95 +0,0 @@
-<template>
-    <u-popup :show="show" :mode="mode" round="30rpx" closeable @close="close" :safeAreaInsetBottom="safeAreaInsetBottom" @open="open">
-        <view class="popup-body pd-30" :class="{ 'bg-jb': hasBg, 'mode-bottom': mode === 'bottom' }" :style="customStyle">
-            <view v-if="showTitle" class="title f-s-30 c-333 f-w-5 mb-30">
-                <slot name="title">
-                    {{ title }}
-                </slot>
-            </view>
-            <view class="f-s-28 f-c-3">
-                <slot name="content">
-					{{ content }}
-				</slot>
-            </view>
-            <view v-if="showFooter" style="padding-top: 20rpx;">
-                <slot name="footer">
-                    <view class="d-flex j-sb">
-                        <u-button color="#F2F2F2" style="color: #333;width: 260rpx;" type="primary" @click="close" :text="cancelText"></u-button>
-                        <u-button style="width: 260rpx;" type="primary" @click="confirm" :text="confirmText"></u-button>
-                    </view>
-                </slot>
-            </view>
-        </view>
-    </u-popup>
-</template>
-
-<script setup lang="ts" name="ut-base-model">
-interface Props {
-    show: boolean;
-    title: string;
-    confirmText: string;
-    cancelText: string;
-    hasBg: boolean;
-    showFooter: boolean;
-    showTitle: boolean;
-    mode: string;
-    safeAreaInsetBottom: boolean;
-    content: string;
-    customStyle: string;
-}
-
-const props = withDefaults(defineProps<Props>(), {
-    show: true,
-    title: '系统提示',
-    confirmText: '确认',
-    cancelText: '取消',
-    hasBg: false,
-    showFooter: true,
-    showTitle: true,
-    mode: 'center',
-    safeAreaInsetBottom: false,
-    content: '',
-    customStyle: ''
-});
-
-const emit = defineEmits<{
-    'update:show': [value: boolean];
-    close: [];
-    open: [];
-    confirm: [];
-}>();
-
-const close = () => {
-    emit("update:show", false);
-    emit("close");
-};
-
-const open = () => {
-    emit("open");
-};
-
-const confirm = () => {
-    emit("confirm");
-    emit("update:show", false);
-};
-</script>
-
-<style lang="scss" scoped>
-.popup-body {
-	width: 88vw;
-	box-sizing: border-box;
-	background-size: contain;
-	background-repeat: no-repeat;
-	background-position: top center;
-
-	&.mode-bottom {
-		width: 100%;
-	}
-
-	&.bg-jb {
-		// 从下到上渐变
-		border-radius: 30rpx;
-		background: linear-gradient(0deg, #EFFAF4 0%, #fff 100%);
-	}
-}
-</style>

+ 0 - 23
src/components/ut-button-add/ut-button-add.vue

@@ -1,23 +0,0 @@
-<template>
-    <up-button @click="clickBtn" :customStyle="{ borderColor: '#AAC5BA' }" color="#F9FDFB">
-        <view class="d-flex a-c c-primary">
-            <image class="small-icon mr-20" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/common/add_icon.png" mode="widthFix" />
-            <slot>
-                {{ text }}
-            </slot>
-        </view>
-    </up-button>
-</template>
-<script setup>
-const props = defineProps({
-  text: {
-    type: String,
-    default: ''
-  }
-});
-const emit = defineEmits(['click']);
-const clickBtn = () => {
-  console.log('clickBtn');
-  emit('click');
-};
-</script>

+ 0 - 154
src/components/ut-checkbox-cln/ut-checkbox-cln.vue

@@ -1,154 +0,0 @@
-<template>
-    <u-popup :show="showModel" :mode="mode" round="30rpx" closeable @close="close" :safeAreaInsetBottom="safeAreaInsetBottom" @open="open">
-        <view class="popup-body pd-30" :class="{ 'bg-jb': hasBg, 'mode-bottom': mode === 'bottom' }" :style="customStyle">
-            <view v-if="showTitle" class="title f-s-30 c-333 f-w-5 mb-30">
-                <slot name="title">
-                    {{ title }}
-                </slot>
-            </view>
-            <scroll-view scroll-y class="content-info">
-                <view class="f-s-28 f-c-3">
-                    <slot name="content">
-                        <template v-for="(item, index) in tabs" :key="index">
-                            <view class="mb-20 pd-20 d-flex a-c checkbox-item_view" @click="clickItem(item.value)" :class="{ checked: checkeds[item.value] }">
-                                <view class="flex1 ov-hd" :class="{ 'c-primary': checkeds[item.value] }">{{ item.label }}</view>
-                                <view @click.stop>
-                                    <up-checkbox activeColor="#2a6d52" usedAlone v-model:checked="checkeds[item.value]"></up-checkbox>
-                                </view>
-                            </view>
-                        </template>
-                    </slot>
-                </view>
-            </scroll-view>
-            <view v-if="showFooter" style="padding-top: 20rpx;">
-                <slot name="footer">
-                    <view class="d-flex j-sb">
-                        <u-button color="#F2F2F2" class="mr-30" style="color: #333;" type="primary" @click="close" :text="cancelText"></u-button>
-                        <u-button  type="primary" @click="confirm" :text="confirmText"></u-button>
-                    </view>
-                </slot>
-            </view>
-        </view>
-    </u-popup>
-</template>
-<script setup>
-import { ref, watch } from 'vue';
-
-const props = defineProps({
-    show: {
-        type: Boolean,
-        default: false
-    },
-    modelValue: {
-        type: Array,
-        default: () => []
-    },
-    title: {
-        type: String,
-        default: '系统提示'
-    },
-    confirmText: {
-        type: String,
-        default: '确认选择'
-    },
-    cancelText: {
-        type: String,
-        default: '取消'
-    },
-    hasBg: {
-        type: Boolean,
-        default: false
-    },
-    showFooter: {
-        type: Boolean,
-        default: true
-    },
-    showTitle: {
-        type: Boolean,
-        default: true
-    },
-    mode: {
-        type: String,
-        default: 'bottom'
-    },
-    customStyle: {
-        type: Object,
-        default: () => ({})
-    },
-    safeAreaInsetBottom: {
-        type: Boolean,
-        default: true
-    },
-    tabs: {
-        type: Array,
-        default: () => []
-    }
-});
-const showModel = ref(props.show);
-const emit = defineEmits(['close', 'confirm', 'open', 'update:show',]);
-const checkeds = ref({});
-const close = () => {
-    emit('update:show', false);
-    emit('close');
-};
-const confirm = () => {
-    // 这里可以处理选中的数据变成数组
-    const selectedValues = Object.keys(checkeds.value).filter(key => checkeds.value[key]);
-    emit('update:modelValue', selectedValues);
-    emit('confirm', selectedValues);
-    emit('update:show', false);
-};
-const open = () => {
-    emit('open');
-};
-const clickItem = (value) => {
-    checkeds.value[value] = !checkeds.value[value];
-};
-watch(
-    () => props.show,
-    (newVal) => {
-        showModel.value = newVal;
-    }
-);
-watch(
-    () => props.modelValue,
-    (newVal) => {
-        checkeds.value = {};
-        newVal.forEach(item => {
-            checkeds.value[item] = true;
-        });
-    },
-    { immediate: true }
-);
-</script>
-<style lang="scss" scoped>
-.popup-body {
-    width: 88vw;
-    box-sizing: border-box;
-    background-size: contain;
-    background-repeat: no-repeat;
-    background-position: top center;
-
-    &.mode-bottom {
-        width: 100%;
-    }
-
-    &.bg-jb {
-        // 从下到上渐变
-        border-radius: 30rpx;
-        background: linear-gradient(0deg, #effaf4 0%, #fff 100%);
-    }
-}
-.content-info {
-    max-height: 60vh;
-    min-height: 200rpx;
-}
-.checkbox-item_view {
-    border: 1rpx solid #e1eee9;
-    border-radius: 10rpx;
-
-    &.checked {
-        border-color: #2a6d52;
-    }
-}
-</style>

+ 0 - 53
src/components/ut-datetime-picker/ut-datetime-picker.vue

@@ -1,53 +0,0 @@
-<template>
-    <view @click.stop="showTime = true" class="ut-datetime-picker" :class="{ 'ut-datetime-picker--border': border }">
-        <slot></slot>
-    </view>
-    <up-datetime-picker v-model:show="showTime" :minDate="minDate" :maxDate="maxDate" ref="datetimePickerRef" :title="title" v-model="form.startTime" :mode="mode" @cancel="cancel" confirmColor="#2a6d52" @confirm="confirm"></up-datetime-picker>
-</template>
-<script setup lang="ts">
-import { parseTime } from '@/utils/ruoyi';
-
-type DateTimeMode = 'time' | 'datetime' | 'date' | 'year-month';
-
-interface Props {
-    title: string;
-    modelValue: string | number;
-    mode: DateTimeMode;
-    border: boolean;
-    hasInput: boolean;
-    minDate: number;
-    maxDate: number;
-}
-
-const props = withDefaults(defineProps<Props>(), {
-    title: '选择时间',
-    modelValue: '',
-    mode: 'datetime' as DateTimeMode,
-    border: false,
-    hasInput: false,
-    minDate: new Date(2020, 0, 1).getTime(),
-    maxDate: new Date(2030, 0, 1).getTime()
-});
-
-const emit = defineEmits<{
-    'update:modelValue': [value: string | null];
-    change: [value: string | null];
-}>();
-
-const showTime = ref(false);
-const datetimePickerRef = ref(null);
-const form = ref({
-    startTime: props.modelValue ? new Date(props.modelValue) : Date.now()
-});
-
-const confirm = (value: { value: number }) => {
-    const startTime = parseTime(value.value, '{y}-{m}-{d} {h}:{i}');
-    showTime.value = false;
-    emit('update:modelValue', startTime);
-    emit('change', startTime);
-};
-
-const cancel = () => {
-    showTime.value = false;
-};
-</script>

+ 0 - 32
src/components/ut-empty/ut-empty.vue

@@ -1,32 +0,0 @@
-<template>
-    <view class="ut-empty d-flex flex-cln j-c a-c pd-30">
-        <view class="ut-empty__icon">
-            <image :style="{ width, height }" :src="image" mode="aspectFit"></image>
-        </view>
-        <view class="ut-empty__text c-ccc f-s-24" style="margin-top: -50rpx;">
-            <slot>
-                {{ text }}
-            </slot>
-        </view>
-    </view>
-</template>
-<script setup>
-const props = defineProps({
-    text: {
-        type: String,
-        default: '暂无数据',
-    },
-    width: {
-        type: String,
-        default: '300rpx',
-    },
-    height: {
-        type: String,
-        default: '300rpx',
-    },
-    image: {
-        type: String,
-        default: 'https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/common/no_data.png',
-    },
-});
-</script>

+ 0 - 64
src/components/ut-file/ut-file.vue

@@ -1,64 +0,0 @@
-<template>
-    <!-- 下载 -->
-    <view :style="{ margin }">
-        <template v-for="file in files" :key="index">
-            <view class="d-flex a-c file-item-box mb-10" @click="downloadFile(file)">
-                <view class="mr-20">
-					<template v-if="['doc', 'docx', 'DOC', 'DOCX'].includes(getFileSuffix(file?.fileName))">
-                       <image class="file-icon" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/common/doc.png" mode="widthFix" />
-					</template>
-					<template v-if="['pdf', 'PDF'].includes(getFileSuffix(file?.fileName))">
-                       <image class="file-icon" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/common/pdf.png" mode="widthFix" />
-					</template>
-                </view>
-                <view class="flex1 ov-hd mr-20">
-                    <view class="f-s-28 c-light-black f-w-5 mb-8">{{ file?.fileName }}</view>
-                    <view v-if="file?.size" class="f-s-24 c-999">大小:{{changeByte(file?.size)}}</view>
-                </view>
-                <view class="d-flex flex-cln">
-                    <image class="small-icon mb-5" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/common/download.png" mode="widthFix" />
-                    <view class="f-s-22 c-primary">下载</view>
-                </view>
-            </view>
-        </template>
-    </view>
-</template>
-<script setup lang="ts">
-// 移除了不必要的ref导入
-import { changeByte, getFileSuffix } from '@/utils/ruoyi';
-import { exportDataFn } from '@/utils/upload';
-
-interface FileItem {
-    fileName: string;
-    size?: number;
-    url?: string;
-}
-
-interface Props {
-    files: FileItem[];
-    margin: string;
-}
-
-const props = withDefaults(defineProps<Props>(), {
-    files: () => [],
-    margin: '0'
-});
-
-const downloadFile = (file: FileItem) => {
-    exportDataFn(file);
-};
-
-</script>
-<style lang="scss" scoped>
-.file-item-box {
-	padding: 24rpx 30rpx;
-	background-color: #fff;
-	border: 1rpx solid $u-border-color;
-	border-radius: 16rpx;
-}
-
-.file-icon {
-	width: 56rpx;
-	height: 56rpx;
-}
-</style>

+ 0 - 139
src/components/ut-files-upload/ut-files-upload.vue

@@ -1,139 +0,0 @@
-<template>
-    <view class="">
-        <up-button v-if="fileList.length < count" class="mb-10" @click="addFiles" :customStyle="{ color: '#2a6d52', backgroundColor: '#F9FDFB', borderColor: '#E1EEE9' }">
-            <up-icon class="mr-5" name="plus-circle" color="#2a6d52" size="30rpx"></up-icon>
-            <span>{{ btnText }}</span>
-        </up-button>
-        <template v-for="(file, index) in fileList" :key="index">
-            <view class="d-flex a-c file-item-box mb-10" @click="downloadFile(file)">
-                <view class="mr-20">
-                    <template v-if="['doc', 'docx', 'DOC', 'DOCX'].includes(getFileSuffix(file?.fileName))">
-                        <image class="file-icon" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/common/doc.png" mode="widthFix" />
-                    </template>
-                    <template v-if="['pdf', 'PDF'].includes(getFileSuffix(file?.fileName))">
-                        <image class="file-icon" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/common/pdf.png" mode="widthFix" />
-                    </template>
-                </view>
-                <view class="flex1 ov-hd mr-20">
-                    <view class="f-s-28 c-light-black f-w-5 mb-8">{{ file?.fileName }}</view>
-                    <view v-if="file?.size" class="f-s-24 c-999">大小:{{changeByte(file?.size)}}</view>
-                </view>
-                <view class="d-flex flex-cln j-c a-c" @click="deleteFile(index)">
-                    <up-icon size="30rpx" color="#f56c6c" name="trash-fill"></up-icon>
-                    <view class="f-s-22 c-danger">删除</view>
-                </view>
-            </view>
-        </template>
-    </view>
-</template>
-
-<script setup>
-import { ref } from 'vue';
-import upload, { exportDataFn } from '@/utils/upload';
-import { changeByte, getFileSuffix } from '@/utils/ruoyi';
-const props = defineProps({
-    modelValue: {
-        type: [Array, Object],
-        default: () => []
-    },
-    btnText: {
-        type: String,
-        default: '上传文件'
-    },
-    disabled: {
-        type: Boolean,
-        default: false
-    },
-	maxSize: {
-		type: Number,
-		default: 10
-	},
-	count: {
-		type: Number,
-		default: 5 // 默认最多上传5个文件
-	},
-	extension: {
-		type: Array,
-		default: () => ['PDF', 'pdf', 'doc', 'docx', 'DOC', 'DOCX'] // 默认支持的文件类型
-	}
-});
-const fileList = ref(props.modelValue || []);
-const emit = defineEmits(['update:modelValue']);
-const addFiles = async () => {
-	if (props.disabled) return;
-	uni.chooseMessageFile({
-		count: props.count - fileList.value.length,
-		type: 'file',
-		extension: props.extension,
-		success (res) {
-			const tempFiles = res.tempFiles;
-            uploadFiles(tempFiles)
-		},
-		fail (err) {
-			console.error(err);
-			uni.showToast({
-				title: '选择文件失败',
-				icon: 'none'
-			});
-		}
-	});
-};
-const deleteFile = (index) => {
-    fileList.value.splice(index, 1);
-    emit('update:modelValue', fileList.value);
-};
-// 上传多个文件
-const uploadFiles = async (files) => {
-    const uploadedFiles = [];
-    for (const file of files) {
-        if (file.size > props.maxSize * 1024 * 1024) {
-            uni.showToast({
-                title: `文件 ${file.name} 超过最大限制 ${props.maxSize}MB`,
-                icon: 'none'
-            });
-            continue;
-        }
-        try {
-            const res = await upload({
-                filePath: file.path,
-                url: '/resource/oss/upload',
-                formData: {
-                    fileName: file.name,
-                }
-            });
-            if (res.code === 200) {
-                uploadedFiles.push({
-                    ...res.data,
-                    fileName: file.name,
-                });
-            } else {
-                uni.showToast({
-                    title: `上传失败: ${res.message}`,
-                    icon: 'none'
-                });
-            }
-        } catch (error) {
-            uni.showToast({
-                title: '上传失败',
-                icon: 'none'
-            });
-        }
-    }
-    fileList.value = [...fileList.value, ...uploadedFiles];
-    emit('update:modelValue', fileList.value);
-};
-</script>
-
-<style lang="scss" scoped>
-.file-item-box {
-	padding: 24rpx 30rpx;
-	background-color: #fff;
-	border: 1rpx solid $u-border-color;
-	border-radius: 16rpx;
-}
-
-.file-icon {
-	width: 56rpx;
-	height: 56rpx;
-}
-</style>

+ 0 - 48
src/components/ut-image/ut-image.vue

@@ -1,48 +0,0 @@
-<template>
-    <template v-if="preview">
-        <view class="ut-image p-rtv" @click.stop="previewImg" :style="{ width, height }">
-            <up-image :src="value" mode="aspectFit" :width="width" :height="height" :showLoading="false" :lazyLoad="lazyLoad"></up-image>
-        </view>
-    </template>
-    <template v-else>
-        <view class="ut-image p-rtv" :style="{ width, height }">
-            <!-- <image :src="value" mode="aspectFit" :style="{ width, height }" :lazy-load="lazyLoad"></image> -->
-            <up-image :src="value" mode="aspectFit" :width="width" :height="height" :showLoading="false" :lazyLoad="lazyLoad"></up-image>
-        </view>
-    </template>
-</template>
-<script setup lang="ts" name="ut-image">
-// 移除了不必要的ref和watch导入
-
-interface Props {
-    width: string;
-    height: string;
-    value: string;
-    preview: boolean;
-    borderRadius: string;
-    lazyLoad: boolean;
-}
-
-const props = withDefaults(defineProps<Props>(), {
-    width: '326rpx',
-    height: '200rpx',
-    value: '',
-    preview: false,
-    borderRadius: '16rpx',
-    lazyLoad: true
-});
-
-const previewImg = () => {
-    if (props.value && props.preview) {
-        uni.previewImage({
-            urls: [props.value]
-        });
-    }
-};
-</script>
-<style lang="scss" scoped>
-.ut-image {
-    border-radius: 16rpx;
-    overflow: hidden;
-}
-</style>

+ 0 - 176
src/components/ut-index-list/ut-index-list.vue

@@ -1,176 +0,0 @@
-<template>
-    <scroll-view class="ut-index-list_scroll" scroll-y scroll-with-animation :scroll-into-view="intoview">
-        <template v-for="(item, index) in list" :key="index">
-            <view class="ut-index-list__item">
-                <view :id="item[letterField] + 'xxxxxx'" style="height: 1px;"></view>
-                <up-sticky :zIndex="10">
-                    <view class="ut-index-list__item__letter">{{ item[letterField] }}</view>
-                </up-sticky>
-                <view class="ut-index-list__item__content">
-                    <slot name="content" :item="item" :index="rowIndex"></slot>
-                </view>
-            </view>
-        </template>
-    </scroll-view>
-    <view class="ut-navber_letter_view d-flex flex-cln j-c j-c">
-        <view class="p-rtv">
-            <view class="ut-navber_letter-box" @touchstart.prevent="touchStart" @touchmove.prevent="touchMove" @touchend.prevent="touchEnd" @touchcancel.prevent="touchEnd">
-                <template v-for="(item, index) in list" :key="index">
-                    <view class="ut-navber_letter p-rtv" :class="{ active: index === cindex }">
-                        {{ item[letterField] }}
-                    </view>
-                </template>
-            </view>
-            <up-transition :show="touching">
-                <view v-if="list[cindex]" class="touching_letter" :style="{ top: setLetter(cindex) }">
-                    <text class="f-w-b c-fff touching_letter_text">{{ list[cindex]?.[letterField] }}</text>
-                </view>
-            </up-transition>
-        </view>
-    </view>
-</template>
-<script setup name="ut-index-list">
-import { sleep } from 'uview-plus';
-import { ref, getCurrentInstance, toRefs, onMounted } from 'vue';
-import { debounce } from 'uview-plus';
-const instance = getCurrentInstance();
-const props = defineProps({
-    list: {
-        type: Array,
-        default: () => []
-    },
-    letterField: {
-        type: String,
-        default: 'firstChar'
-    },
-    letterItem: {
-        type: String,
-        default: 'rows'
-    },
-    showLetter: {
-        type: Object,
-        default: () => ({})
-    }
-});
-const intoview = ref('');
-const itemHeight = ref(uni.$u.getPx('36rpx'));
-const letterHeight = ref(uni.$u.getPx('100rpx'));
-const letterInfo = ref({});
-const touching = ref(false);
-// 根据e.target.offsetTop计算出当前滚动到的位置
-// 获取index选中
-const getSelectIndex = (pageY) => {
-    let { top, height } = letterInfo.value;
-    let index = 0;
-    if (pageY < top) {
-        index = 0;
-    } else if (pageY >= top + height) {
-        // 如果超出了,取最后一个字母
-        index = props.list.length - 1;
-    } else {
-        index = Math.floor((pageY - top) / itemHeight.value);
-    }
-    if (index > props.list.length - 1) {
-        index = props.list.length - 1;
-    }
-    return index;
-};
-const setLetter = (index) => {
-    let scrollTop = itemHeight.value * index - (letterHeight.value - itemHeight.value) / 2;
-    return scrollTop + 'px';
-};
-// 设置index选中
-const setSelectIndex = (index) => {
-    // letter.value = props.list[index][props.letterField];
-    intoview.value = props.list[index][props.letterField] + 'xxxxxx';
-};
-const cindex = ref(-1);
-const touchStart = (e) => {
-    const touchData = e.changedTouches[0];
-    if (!touchData) return;
-    touching.value = true;
-    cindex.value = getSelectIndex(touchData.pageY);
-};
-const touchMove = (e) => {
-    const touchData = e.changedTouches[0];
-    if (!touchData) return;
-    if (!touching.value) {
-        touching.value = true;
-    }
-    cindex.value = getSelectIndex(touchData.pageY);
-};
-const touchEnd = (e) => {
-    // letter.value = '';
-    sleep(500).then(() => {
-        touching.value = false;
-        setSelectIndex(cindex.value);
-        cindex.value = -1;
-    });
-};
-
-onMounted(() => {
-    setTimeout(() => {
-        const query = uni.createSelectorQuery().in(instance.proxy);
-        query
-            .select('.ut-navber_letter-box')
-            .boundingClientRect((data) => {
-                letterInfo.value = data;
-            })
-            .exec();
-    }, 500);
-});
-</script>
-<style lang="scss" scoped>
-.ut-index-list_scroll {
-    height: 100%;
-}
-
-.ut-index-list__item__letter {
-    padding: 10rpx 0;
-    font-size: 26rpx;
-    color: #999;
-    background-color: #fff;
-}
-
-.ut-navber_letter_view {
-    position: absolute;
-    right: 0;
-    top: 0;
-    bottom: 0;
-    width: 36rpx;
-    z-index: 30;
-
-    .ut-navber_letter {
-        font-size: 24rpx;
-        color: #333;
-        width: 36rpx;
-        height: 36rpx;
-        text-align: center;
-
-        &.active {
-            background-color: $u-primary;
-            border-radius: 50%;
-            color: #fff;
-        }
-    }
-}
-
-.touching_letter {
-    position: absolute;
-    right: 60rpx;
-    width: 100rpx;
-    height: 100rpx;
-    border-radius: 200rpx 200rpx 0 200rpx;
-    background-color: #c9c9c9;
-    transform: rotate(-45deg);
-    display: flex;
-    justify-content: center;
-    align-items: center;
-
-    .touching_letter_text {
-        line-height: 1;
-        font-size: 56rpx;
-        transform: rotate(45deg);
-    }
-}
-</style>

+ 0 - 27
src/components/ut-loading-view/ut-loading-view.vue

@@ -1,27 +0,0 @@
-<template>
-    <view class="p-rtv">
-        <slot></slot>
-        <view v-if="loading" class="loading-box d-flex a-c j-c">
-            <up-loading-icon color="#2A6D52"></up-loading-icon>
-        </view>
-    </view>
-</template>
-<script setup>
-const props = defineProps({
-    loading: {
-        type: Boolean,
-        default: false
-    }
-})
-</script>
-<style lang="scss" scoped>
-.loading-box {
-    position: absolute;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    background-color: rgba(255, 255, 255, .8);
-    z-index: 9999;
-}
-</style>

+ 0 - 66
src/components/ut-marquee-animation/ut-marquee-animation.vue

@@ -1,66 +0,0 @@
-<template>
-    <view class="css-swiper p-rtv ov-hd" :style="{ height: auto ? setheight(count, list.length, height) : 'auto' }">
-        <view class="css-swiper-inner" :class="{ 'swiper-animation': auto && list.length > count }" :style="{ animationDuration: interval * list.length * 1.5 + 'ms' }">
-            <template v-if="auto && list.length > count">
-                <template v-for="(item, index) in list.concat(list)" :key="index">
-                    <slot name="item" :item="item"></slot>
-                </template>
-            </template>
-            <template v-else>
-                <template v-for="(item, index) in list" :key="index">
-                    <slot name="item" :item="item"></slot>
-                </template>
-            </template>
-        </view>
-    </view>
-</template>
-<script setup name="ut-marquee-animation">
-import { ref, onMounted, getCurrentInstance } from 'vue';
-const instance = getCurrentInstance();
-const props = defineProps({
-    list: {
-        type: Array,
-        default: () => []
-    },
-    count: {
-        type: Number,
-        default: 6
-    },
-    auto: {
-        type: Boolean,
-        default: true
-    },
-    interval: {
-        type: Number,
-        default: 2000
-    },
-    height: {
-        type: Number,
-        default: 110
-    }
-})
-const setheight = (count, len, height) => {
-    if (len > count) {
-        return count * height + 'rpx'
-    } else {
-        return 'auto'
-    }
-}
-</script>
-<style lang="scss" scoped>
-.css-swiper-inner {
-    &.swiper-animation {
-        animation: marqueeAnimation linear infinite;
-    }
-}
-
-@keyframes marqueeAnimation {
-    0% {
-        transform: translateY(0);
-    }
-
-    100% {
-        transform: translateY(-50%);
-    }
-}
-</style>

+ 0 - 98
src/components/ut-navbar-model/ut-navbar-model.vue

@@ -1,98 +0,0 @@
-<template>
-    <view id="navbarModel" class="navbar-model"></view>
-    <view v-show="show" @click="close" class="transition model-drop" :style="{ top: modelPtn.top + 'px' }">
-        <view @click.stop class="model-drop-content pd-24" :class="{}">
-            <slot name="title">
-                <view class="f-s-32 c-999 mb-16">{{ title }}</view>
-            </slot>
-            <slot></slot>
-            <view v-if="showFooter">
-                <slot name="footer">
-                    <view class="d-flex j-sb" style="padding-top: 20rpx;">
-                        <u-button color="#F2F2F2" style="color: #333;" type="primary" @click="close" :text="cancelText"></u-button>
-                        <view class="pd-10"></view>
-                        <u-button type="primary" @click="confirm" :text="confirmText"></u-button>
-                    </view>
-                </slot>
-            </view>
-        </view>
-    </view>
-</template>
-<script setup name="ut-navbar-model">
-import { ref, onMounted, getCurrentInstance, watch } from 'vue';
-const vsible = ref(false);
-const emit = defineEmits(['confirm', 'update:show', 'close']);
-const instance = getCurrentInstance();
-const props = defineProps({
-    show: {
-        type: Boolean,
-        default: false
-    },
-    width: {
-        type: String,
-        default: '100%'
-    },
-    title: {
-        type: String,
-        default: ''
-    },
-    showFooter: {
-        type: Boolean,
-        default: true
-    },
-    // 确认文字
-    confirmText: {
-        type: String,
-        default: '确认'
-    },
-    // 取消文字
-    cancelText: {
-        type: String,
-        default: '取消'
-    }
-});
-const modelPtn = ref({
-    top: 0
-})
-const close = () => {
-    emit('update:show', false);
-    emit('close');
-}
-const confirm = () => {
-    emit('confirm', true);
-    emit('update:show', false);
-}
-onMounted(() => {
-    const query = uni.createSelectorQuery().in(instance.proxy);
-    query
-        .select(".navbar-model")
-        .boundingClientRect((data) => {
-            modelPtn.value = data;
-        })
-        .exec();
-})
-watch(() => props.show, (val) => {
-    vsible.value = val;
-}, { immediate: true });
-</script>
-<style lang="scss" scoped>
-.navbar-model {
-    position: relative;
-    height: 1rpx;
-}
-
-.model-drop {
-    position: fixed;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    background-color: rgba(0, 0, 0, .6);
-    z-index: 999;
-}
-
-.model-drop-content {
-    box-sizing: border-box;
-    width: 100%;
-    background-color: #fff;
-}
-</style>

+ 0 - 10
src/components/ut-navbar/ut-navbar.vue

@@ -1,10 +0,0 @@
-<template>
-	<view class="">xx</view>
-</template>
-
-<script>
-	
-</script>
-
-<style>
-</style>

+ 0 - 63
src/components/ut-picker-date/ut-picker-date.vue

@@ -1,63 +0,0 @@
-<template>
-    <picker :value="date" mode="date" :start="start" :end="end" @change="onChange" :fields="fields">
-        <slot>
-            <view class="date-item" :style="cStyle">
-                <text v-if="date" class="c-333 f-s-26">{{ date }}</text>
-                <text v-else class="c-999 f-s-26">{{ placeholder }}</text>
-            </view>
-        </slot>
-    </picker>
-</template>
-<script setup name="ut-picker-date">
-import { watch, ref } from 'vue';
-const props = defineProps({
-    modelValue: {
-        type: String,
-        default: ''
-    },
-    placeholder: {
-        type: String,
-        default: '请选择日期'
-    },
-    disabled: {
-        type: Boolean,
-        default: false
-    },
-    fields: {
-        type: String,
-        default: 'date'
-    },
-    cStyle: {
-        type: String,
-        default: ''
-    },
-    start: {
-        type: String,
-        default: ''
-    },
-    end: {
-        type: String,
-        default: ''
-    }
-})
-const emit = defineEmits(['update:modelValue']);
-const date = ref(props.modelValue);
-const onChange = (e) => {
-    date.value = e.detail.value;
-    emit('update:modelValue', date.value);
-    emit('change', date.value);
-}
-watch(() => props.modelValue, (newVal) => {
-    date.value = newVal;
-});
-</script>
-<style lang="scss" scoped>
-.date-item {
-    width: 100%;
-    height: 54rpx;
-    text-align: center;
-    line-height: 54rpx;
-    background-color: #f7f7f7;
-    border-radius: 8rpx;
-}
-</style>

+ 0 - 73
src/components/ut-picker-region/ut-picker-region.vue

@@ -1,73 +0,0 @@
-<template>
-    <picker mode="region" @change="regionChange" :value="region" custom-item="全部" level="region">
-        <view class="d-flex">
-            <up-input class="flex1" v-model="regiontext" :placeholder="placeholder" border="none" readonly></up-input>
-            <up-icon color="#2A6D52" size="20rpx" style="transform: rotate(90deg);" name="play-right-fill"></up-icon>
-        </view>
-    </picker>
-</template>
-<script setup lang="ts" name="ut-picker-region">
-// 移除了未使用的API导入,如果需要使用API,可以导入 useClientRequest
-
-interface RegionData {
-    adcodeName?: string;
-    adcode?: string;
-    province?: string;
-    city?: string;
-    district?: string;
-}
-
-interface Props {
-    modelValue: RegionData;
-    defaultText: string;
-    placeholder: string;
-}
-
-const props = withDefaults(defineProps<Props>(), {
-    modelValue: () => ({}),
-    defaultText: '',
-    placeholder: '请选择地区'
-});
-
-const emit = defineEmits<{
-    change: [value: RegionData];
-    'update:modelValue': [value: RegionData];
-}>();
-
-const region: string[] = [];
-const regiontext = ref('');
-
-const regionChange = (e: { detail: { code: string[]; value: string[] } }) => {
-    const { code, value } = e.detail;
-    console.log(code, value);
-    // region.value = value;  // region is not reactive
-    regiontext.value = value.join('');
-    
-    const obj: RegionData = {
-        ...form.value,
-        adcodeName: value.join(''),
-        adcode: code[code.length - 1],
-        province: value[0],
-        city: value[1],
-        district: value[2]
-    };
-    
-    emit('update:modelValue', obj);
-    emit('change', obj);
-};
-
-const form = ref<RegionData>({});
-
-watch(() => props.modelValue, (ov) => {
-    if (ov) {
-        form.value = { ...ov };
-        regiontext.value = (ov?.province || '') + (ov?.city || '') + (ov?.district || '');
-    }
-}, { immediate: true });
-
-watch(() => props.defaultText, (ov) => {
-    if (ov) {
-        regiontext.value = regiontext.value || ov;
-    }
-}, { immediate: true });
-</script>

+ 0 - 173
src/components/ut-picker/ut-picker.vue

@@ -1,173 +0,0 @@
-<template>
-    <view @click.stop="showModel = true">
-        <slot></slot>
-    </view>
-    <u-popup :show="showModel" :mode="mode" round="30rpx" closeable @close="close" :safeAreaInsetBottom="safeAreaInsetBottom" @open="open">
-        <view class="popup-body pd-30" :class="{ 'bg-jb': hasBg, 'mode-bottom': mode === 'bottom' }" :style="customStyle">
-            <view v-if="showTitle" class="title f-s-30 c-333 f-w-5 mb-30">
-                <slot name="title">
-                    {{ title }}
-                </slot>
-            </view>
-            <scroll-view scroll-y class="content-info">
-                <view class="f-s-28 f-c-3">
-                    <slot name="content">
-                        <template v-for="(item, index) in tabs" :key="index">
-                            <view class="mb-20 pd-20 d-flex a-c checkbox-item_view" @click="clickItem(item.value)" :class="{ checked: checkeds[item.value] }">
-                                <view class="flex1 ov-hd" :class="{ 'c-primary': checkeds[item.value] }">{{ item.label }}</view>
-                                <view @click.stop>
-                                    <up-checkbox activeColor="#2a6d52" usedAlone v-model:checked="checkeds[item.value]"></up-checkbox>
-                                </view>
-                            </view>
-                        </template>
-                    </slot>
-                </view>
-            </scroll-view>
-            <view v-if="showFooter" style="padding-top: 20rpx;">
-                <slot name="footer">
-                    <view class="d-flex j-sb">
-                        <u-button color="#F2F2F2" class="mr-30" style="color: #333;" type="primary" @click="close" :text="cancelText"></u-button>
-                        <u-button type="primary" @click="confirm" :text="confirmText"></u-button>
-                    </view>
-                </slot>
-            </view>
-        </view>
-    </u-popup>
-</template>
-<script setup>
-import { ref, watch } from 'vue';
-
-const props = defineProps({
-    show: {
-        type: Boolean,
-        default: false
-    },
-    modelValue: {
-        type: Array,
-        default: () => []
-    },
-    str: {
-        type: String,
-        default: ''
-    },
-    title: {
-        type: String,
-        default: '系统提示'
-    },
-    confirmText: {
-        type: String,
-        default: '确认选择'
-    },
-    cancelText: {
-        type: String,
-        default: '取消'
-    },
-    hasBg: {
-        type: Boolean,
-        default: false
-    },
-    showFooter: {
-        type: Boolean,
-        default: true
-    },
-    showTitle: {
-        type: Boolean,
-        default: true
-    },
-    mode: {
-        type: String,
-        default: 'bottom'
-    },
-    customStyle: {
-        type: Object,
-        default: () => ({})
-    },
-    safeAreaInsetBottom: {
-        type: Boolean,
-        default: true
-    },
-    tabs: {
-        type: Array,
-        default: () => []
-    },
-    valueType: {
-        type: String,
-        default: 'arr'
-    }
-});
-const showModel = ref(props.show);
-const emit = defineEmits(['close', 'confirm', 'open', 'update:show', 'update:modelValue', 'update:str', 'change']);
-const checkeds = ref({});
-const close = () => {
-    showModel.value = false;
-    emit('update:show', false);
-    emit('close');
-};
-const confirm = () => {
-    // 这里可以处理选中的数据变成数组
-    const selectedValues = Object.keys(checkeds.value).filter((key) => checkeds.value[key]);
-    emit('update:modelValue', selectedValues);
-    emit('update:str', selectedValues.join(','));
-    emit('change', selectedValues);
-    showModel.value = false;
-    emit('update:show', false);
-};
-const open = () => {
-    emit('open');
-};
-const clickItem = (value) => {
-    checkeds.value[value] = !checkeds.value[value];
-};
-
-watch(
-    () => props.modelValue,
-    (newVal) => {
-        checkeds.value = {};
-        newVal.forEach((item) => {
-            checkeds.value[item.value] = true;
-        });
-    },
-    { immediate: true }
-);
-watch(
-    () => props.str,
-    (newVal) => {
-        if (!newVal) return;
-        checkeds.value = {};
-        newVal.split(',').forEach((item) => {
-            checkeds.value[item] = true;
-        });
-    }
-);
-</script>
-<style lang="scss" scoped>
-.popup-body {
-    width: 88vw;
-    box-sizing: border-box;
-    background-size: contain;
-    background-repeat: no-repeat;
-    background-position: top center;
-
-    &.mode-bottom {
-        width: 100%;
-    }
-
-    &.bg-jb {
-        // 从下到上渐变
-        border-radius: 30rpx;
-        background: linear-gradient(0deg, #effaf4 0%, #fff 100%);
-    }
-}
-.content-info {
-    max-height: 60vh;
-    min-height: 200rpx;
-}
-.checkbox-item_view {
-    border: 1rpx solid #e1eee9;
-    border-radius: 10rpx;
-
-    &.checked {
-        border-color: #2a6d52;
-    }
-}
-</style>

+ 0 - 198
src/components/ut-pickup-date/ut-pickup-date.vue

@@ -1,198 +0,0 @@
-<template>
-    <u-popup :show="show" :mode="mode" round="30rpx" closeable @close="close" :safeAreaInsetBottom="safeAreaInsetBottom" @open="open">
-        <view class="popup-body pd-30" :class="{ 'bg-jb': hasBg, 'mode-bottom': mode === 'bottom' }">
-            <view class="title f-s-30 c-333 f-w-5 mb-30">
-                {{ title }}
-            </view>
-            <view class="f-s-28 f-c-3 pickup-date-content d-flex">
-                <scroll-view class="left-scroll" scroll-y>
-                    <view>
-                        <template v-for="(item, index) in dates" :key="index">
-                            <view @click="changeLeftNavs(item)" class="left-item-nav pd-20 d-flex a-c j-c" :class="{ checked: item.date === form.date }">{{ item.alias }}</view>
-                        </template>
-                    </view>
-                </scroll-view>
-                <scroll-view class="flex1 right-scroll" scroll-y>
-                    <view class="pd-20">
-                        <up-radio-group v-model="form.time" placement="column" @change="groupChange">
-                            <up-radio v-for="(item, index) in times" activeColor="#2A6D52" :key="index" :name="item.end" :label="item.alias">{{ item.alias }}</up-radio>
-                        </up-radio-group>
-                    </view>
-                </scroll-view>
-            </view>
-            <view v-if="showFooter" style="padding-top: 20rpx;">
-                <slot name="footer">
-                    <view class="d-flex j-sb">
-                        <u-button color="#F2F2F2" style="color: #333;width: 260rpx;" type="primary" @click="close" :text="cancelText"></u-button>
-                        <u-button style="width: 260rpx;" type="primary" @click="confirm" :text="confirmText"></u-button>
-                    </view>
-                </slot>
-            </view>
-        </view>
-    </u-popup>
-</template>
-
-<script setup>
-import { ref, watch, defineProps, defineEmits } from 'vue'
-
-const props = defineProps({
-	show: {
-		type: Boolean,
-		default: true,
-	},
-	title: {
-		type: String,
-		default: '请选择上门取件时间'
-	},
-	confirmText: {
-		type: String,
-		default: '确认'
-	},
-	cancelText: {
-		type: String,
-		default: '取消'
-	},
-	hasBg: {
-		type: Boolean,
-		default: false,
-	},
-	showFooter: {
-		type: Boolean,
-		default: true,
-	},
-	mode: {
-		type: String,
-		default: 'center',
-	},
-	safeAreaInsetBottom: {
-		type: Boolean,
-		default: false,
-	},
-})
-const dates = ref([])
-const times = ref([])
-const activeLeft = ref('')
-const form = ref({
-    date: '',
-    time: ''
-})
-const emit = defineEmits(['update:show', 'close', 'open', 'confirm', 'change'])
-
-const showModel = ref(false)
-// 获取从今天开始的一周日期, 日期格式为:2021-09-01,如果是今天,明天,后天,其余别名当前日期,日期别名为:今天,明天,后天,数组元素为 { date: '2021-09-01', alias: '今天' }
-const getDates = () => {
-    const date = new Date()
-    const dates = []
-    for (let i = 0; i < 7; i++) {
-        const item = {}
-        const curDate = new Date(date.getTime() + i * 24 * 60 * 60 * 1000)
-        const curDateStr = curDate.toISOString().split('T')[0]
-        if (i === 0) {
-            item.alias = '今天'
-        } else if (i === 1) {
-            item.alias = '明天'
-        } else if (i === 2) {
-            item.alias = '后天'
-        } else {
-            item.alias = curDateStr
-        }
-        item.date = curDateStr
-        dates.push(item)
-    }
-    return dates
-}
-// 根据日期获取时间段如果传入的日期是今天,时间段为当前时间之后的时间段,如果是明天,后天,时间段为早上8点到晚上20点,时间段格式为:08:00~09:00,09:00~10:00, 值是后一位时间 间隔传入参数n分钟数组元素为 { start: '08:00', end: '09:00', alias: '08:00~09:00' }
-// 当前时间之后m分钟
-const getTimes = (date, n = 60, m = 60) => {
-    const times = []
-    const curDate = new Date()
-    const curDateStr = curDate.toISOString().split('T')[0]
-    const curHour = curDate.getHours()
-    const curMinute = curDate.getMinutes()
-    const curTime = curHour * 60 + curMinute
-    let start = 8
-    let end = 20
-    if (date === curDateStr) {
-        start = curHour + 1
-        if (curMinute > 30) {
-            start += 1
-        }
-    }
-    for (let i = start; i < end; i++) {
-        const item = {}
-        item.start = `${i < 10 ? '0' + i : i}:00`
-        item.end = `${i + 1 < 10 ? '0' + (i + 1) : i + 1}:00`
-        item.alias = `${item.start}~${item.end}`
-        times.push(item)
-    }
-    return times
-}
-watch(() => props.show, (val) => {
-	showModel.value = val
-}, { immediate: true })
-
-const close = () => {
-	emit("update:show", false)
-	emit("close")
-}
-const changeLeftNavs = (item) => {
-    form.value.date = item.date
-    times.value = getTimes(item.date)
-    form.value.time = ''
-}
-const open = () => {
-    dates.value = getDates()
-    form.value.date = dates.value[0].date
-    times.value = getTimes(form.value.date)
-	emit("open")
-}
-
-const confirm = () => {
-    const obj = {
-        date: form.value.date,
-        time: form.value.time,
-        dateAlias: dates.value.find(item => item.date === form.value.date).alias,
-        timeAlias: times.value.find(item => item.end === form.value.time).alias
-    }
-    emit('change', obj)
-    emit("confirm", obj)
-	emit("update:show", false)
-}
-</script>
-
-<style lang="scss" scoped>
-.popup-body {
-	width: 86vw;
-	box-sizing: border-box;
-
-	&.mode-bottom {
-		width: 100%;
-	}
-
-	&.bg-jb {
-		// 从下到上渐变
-		border-radius: 30rpx;
-		background: linear-gradient(0deg, #EFFAF4 0%, #fff 100%);
-	}
-}
-.pickup-date-content {
-    height: 40vh;
-}
-
-.left-scroll {
-    width: 260rpx;
-    height: 100%;
-    background-color: #f4f4f4;
-}
-.right-scroll {
-    height: 100%;
-    background-color: #f9f9f9;
-}
-
-.left-item-nav {
-  &.checked {
-    background-color: #2A6D52;
-    color: #fff;
-  }
-}
-</style>

+ 0 - 99
src/components/ut-search/ut-search.vue

@@ -1,99 +0,0 @@
-<template>
-    <!-- 搜索框 -->
-    <view class="search-input d-flex a-c" :class="{ 'up-border': border }" :style="{ margin, background: bgColor, height }">
-        <up-input v-model="value" ref="searchInputRef" clearable type="text" :focused="focused" border="none" @change="inputSearch" @clear="clear" @confirm="search" confirmType="search" :fontSize="fontSize" :maxlength="maxlength" :placeholder="placeholder">
-            <template #suffix>
-                <view @click.stop="search" class="d-flex j-c a-c" style="padding: 0 26rpx;">
-                    <image class="search_icon" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/common/search_icon.png" mode="widthFix" />
-                </view>
-            </template>
-        </up-input>
-    </view>
-</template>
-<script setup name="search">
-import { defineProps, defineEmits, ref, watch } from 'vue';
-import { debounce } from 'uview-plus';
-const props = defineProps({
-    modelValue: {
-        type: String,
-        default: ''
-    },
-    margin: {
-        type: String,
-        default: '24rpx'
-    },
-    placeholder: {
-        type: String,
-        default: '请输入搜索内容'
-    },
-    maxlength: {
-        type: Number,
-        default: 140
-    },
-    fontSize: {
-        type: String,
-        default: '30rpx'
-    },
-    height: {
-        type: String,
-        default: '86rpx'
-    },
-    border: {
-        type: Boolean,
-        default: true
-    },
-    // 背景颜色
-    bgColor: {
-        type: String,
-        default: '#fff'
-    }
-});
-const value = ref(props.modelValue);
-const emit = defineEmits(['search', 'update:modelValue', 'change']);
-const inputSearch = (e) => {
-    debounce(() => {
-        emit('update:modelValue', e);
-        emit('change', e);
-    }, 500);
-};
-const search = (e) => {
-    debounce(() => {
-        emit('update:modelValue', value.value);
-        emit('search', value.value);
-    }, 500);
-};
-const clear = () => {
-    emit('update:modelValue', '');
-    emit('change', '');
-    emit('search', '');
-};
-const searchInputRef = ref(null);
-const focused = ref(false);
-const doFocus = () => {
-    // focused.value = true;
-    searchInputRef.value.doFocus();
-};
-// 监听输入框的输入事件,触发更新modelValue的值
-watch(
-    () => props.modelValue,
-    (val) => {
-        value.value = val;
-    }
-);
-defineExpose({
-    doFocus
-});
-</script>
-<style lang="scss" scoped>
-.search-input {
-    height: 86rpx;
-    background-color: #fff;
-    border-radius: 84rpx;
-    padding-left: 30rpx;
-}
-
-.search_icon {
-    width: 38rpx;
-    height: 38rpx;
-}
-</style>

+ 0 - 34
src/components/ut-select-down/ut-select-down.vue

@@ -1,34 +0,0 @@
-<template>
-    <view @click.stop="clickItem" class="d-flex a-c flex1 ov-hd">
-        <view v-if="text" class="f-s-36 c-333 mr-10">
-            <slot>
-                {{ text }}
-            </slot>
-        </view>
-        <view v-else class="f-s-36 c-999 mr-10">{{ placeholder }}</view>
-        <view class="right-fill-icon">
-          <up-icon color="#333" size="26rpx" name="play-right-fill"></up-icon>
-        </view>
-    </view>
-</template>
-<script setup name="ut-select-down">
-const emit = defineEmits(['click'])
-const props = defineProps({
-    text: {
-        type: String,
-        default: ''
-    },
-    placeholder: {
-        type: String,
-        default: '请选择规格'
-    }
-})
-const clickItem = () => {
-    emit('click')
-}
-</script>
-<style lang="scss" scoped>
-.right-fill-icon {
-    transform: rotate(90deg);
-}
-</style>

+ 0 - 122
src/components/ut-tabar/ut-tabar.vue

@@ -1,122 +0,0 @@
-<template>
-    <view class="tabar-wrap fixded-tabar">
-        <image class="btm_bg" src="https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/bottom/btm_bg.png" mode="aspectFit" />
-        <view class="tabar-box d-flex">
-            <template v-for="(item, index) in navs" :key="index">
-                <view v-if="!item.v" class="d-flex flex-cln j-c a-c tabar-item flex1" @click="goPage(item.url)">
-                    <image class="tabar-icon" :src="mapTabarIcon(item)" mode="aspectFit" />
-                    <view class="taber-text f-s-26" :class="{ 'active': item.key === value }">{{ item.name }}</view>
-                </view>
-                <view v-else class="d-flex j-c a-c tabar-item flex1 flex-cln p-rtv" @click="goPage(item.url)">
-                    <image class="tabar-v-icon" :src="mapTabarIcon(item)" mode="aspectFit" />
-                    <view class="dot" :class="{ 'active': item.key === value }"></view>
-                </view>
-            </template>
-        </view>
-        <!-- <view class="pd-10 bg-fff"></view> -->
-        <view class="safe-area bg-fff"></view>
-    </view>
-</template>
-
-<script setup name="ut-tabar">
-import { ref, onMounted, getCurrentInstance } from 'vue';
-const instance = getCurrentInstance();
-const props = defineProps({
-    bgColor: {
-        type: String,
-        default: '#fff'
-    },
-    value: {
-        type: Number,
-        default: 'home'
-    }
-})
-const mapTabarIcon = (item) => {
-    const { icon, key, v } = item
-    if (key === props.value && !v) {
-        return `https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/bottom/${icon}1.png`
-    } else {
-        return `https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/bottom/${icon}.png`
-    }
-}
-const navs = ref([
-    { name: '价格', icon: 'home', url: '/pages/index/index', key: 'home' },
-    { name: '行情', icon: 'find', url: '/pages/find/index', key: 'find' },
-    { name: '商城', icon: 'cart', url: '/pages/cart/index', key: 'cart', v: true },
-    { name: '服务', icon: 'list', url: '/pages/list/index', key: 'list' },
-    { name: '我的', icon: 'user', url: '/pages/user/index', key: 'user' },
-])
-const goPage = (url) => {
-    uni.$u.route({
-        type: 'switchTab',
-        url
-    })
-}
-const emit = defineEmits(['tabarheight'])
-onMounted(() => {
-    const query = uni.createSelectorQuery().in(instance.proxy);
-    query
-        .select(".fixded-tabar")
-        .boundingClientRect((data) => {
-           emit('tabarheight', data.height)
-        })
-        .exec();
-})
-</script>
-
-<style lang="scss" scoped>
-.tabar-wrap {
-    padding-top: 80rpx;
-}
-
-.btm_bg {
-    position: absolute;
-    width: 750rpx;
-    height: 204rpx;
-    left: 0;
-    right: 0;
-    top: 0;
-    // 去掉点击事件
-    pointer-events: none;
-    user-select: none;
-}
-
-.tabar-box {
-    position: relative;
-    height: 100rpx;
-}
-
-.tabar-icon {
-    width: 60rpx;
-    height: 60rpx;
-}
-
-.tabar-v-icon {
-    width: 116rpx;
-    height: 116rpx;
-    transform: translateY(-10rpx);
-}
-
-.dot {
-    width: 8rpx;
-    height: 8rpx;
-    border-radius: 50%;
-    background: transparent;
-
-    &.active {
-        background: $u-primary;
-    }
-}
-.fixded-tabar {
-    position: fixed;
-    left: 0;
-    right: 0;
-    bottom: 0;
-}
-.taber-text {
-    color: #CDCDCD;
-    &.active {
-        color: $u-primary;
-    }
-}
-</style>

+ 0 - 68
src/components/ut-tabs-card/ut-tabs-card.vue

@@ -1,68 +0,0 @@
-<template>
-    <view class="ut-tabs-card d-flex a-c j-sb p-rtv" :style="cStyle">
-        <template v-for="(item, index) in tabs" :key="index">
-            <view class="tabs-item-card d-flex a-c"  @click="clickItem(item.value)" :class="{ checked: checked === item.value }">
-                <view class="left-ball">
-                    <image style="width: 50rpx; height: 50rpx;" :src="`https://ta.zycpzs.cn/oss-file/smart-trace/szyy/images/price/${ checked === item.value ? item.icon + 1 : item.icon }.png`" mode="widthFix" />
-                </view>
-                <view class="pl-10 pr-14 f-s-28">{{ item.label }}</view>
-            </view>
-        </template>
-    </view>
-</template>
-<script setup name="ut-tabs-card">
-import { defineProps, ref, watch, onMounted, getCurrentInstance } from 'vue';
-const instance = getCurrentInstance();
-const emit = defineEmits(['change', 'update:modelValue'])
-const props = defineProps({
-    tabs: {
-        type: Array,
-        default: () => []
-    },
-    modelValue: {
-        type: String,
-        default: ''
-    },
-    cStyle: {
-        type: Object,
-        default: () => ({})
-    }
-})
-const checked = ref('')
-const clickItem = (val) => {
-    emit('update:modelValue', val)
-    emit('change', val)
-}
-
-onMounted(() => {
-    checked.value = props.modelValue
-})
-watch(() => props.modelValue, (val) => {
-    checked.value = val
-}, { immediate: true })
-</script>
-<style lang="scss" scoped>
-.ut-tabs-card {
-    padding: 24rpx 14rpx;
-}
-.tabs-item-card {
-    margin: 0 10rpx;
-    border-radius: 27rpx;
-    color: #999;
-    font-size: 28rpx;
-    background-color: #F2F2F2;
-    border: 2rpx solid #F2F2F2;
-
-    &.checked {
-        color: #fff;
-        background-color: $u-primary;
-        border-color: $u-primary;
-    }
-}
-
-.left-ball {
-    width: 50rpx;
-    height: 50rpx;
-    border-radius: 50%;
-}
-</style>

+ 0 - 114
src/components/ut-tabs-dict/ut-tabs-dict.vue

@@ -1,114 +0,0 @@
-<template>
-    <template v-if="cStyle === 'fill'">
-        <up-tabs ref="upTabsRef" class="fill-tabs" :list="tabs" keyName="label" @change="change"
-            lineColor="rgba(0,0,0,0)">
-            <template #content="{ item, keyName, index }">
-                <view class="tab-fill" :style="{ background: index === current ? lineColor : '#F7F7F7' }"
-                    :class="{ checked: index === current }">{{ item[keyName] }}</view>
-            </template>
-        </up-tabs>
-    </template>
-    <template v-else>
-        <up-tabs ref="upTabsRef" :list="tabs" keyName="label" @change="change" :lineWidth="curLineWidth"
-            :current="current" :lineHeight="lineHeight" :itemStyle="itemStyle" :lineColor="lineColor"
-            :inactiveStyle="inactiveStyle" :activeStyle="activeStyle">
-        </up-tabs>
-    </template>
-</template>
-<script setup name="ut-tabs-dict">
-import { toRefs, ref, onMounted, watch } from 'vue';
-const props = defineProps({
-    tabs: {
-        type: Array,
-        default: () => []
-    },
-    modelValue: {
-        type: String,
-        default: ''
-    },
-    inactiveStyle: {
-        type: Object,
-        default: () => ({
-            color: '#999',
-            fontSize: '30rpx'
-        })
-    },
-    activeStyle: {
-        type: Object,
-        default: () => ({
-            color: '#2A6D52',
-            fontSize: '30rpx',
-            fontWeight: 500
-        })
-    },
-    lineHeight: {
-        type: String,
-        default: '2px'
-    },
-    lineWidth: {
-        type: String,
-        default: '60rpx'
-    },
-    fontSize: {
-        type: String,
-        default: '30rpx'
-    },
-    itemStyle: {
-        type: Object,
-        default: () => ({
-            padding: '10rpx 20rpx'
-        })
-    },
-    isAutoWidth: {
-        type: Boolean,
-        default: false
-    },
-    lineColor: {
-        type: String,
-        default: '#2A6D52'
-    },
-    cStyle: {
-        type: String,
-        default: ''
-    }
-});
-const upTabsRef = ref(null);
-const emit = defineEmits(['change', 'update:modelValue']);
-const change = (item) => {
-    emit('update:modelValue', item.value);
-    emit('change', item.value);
-};
-const setCurLineWidthset = (index) => {
-    const item = props.tabs[index];
-    const width = item?.label?.length * 30;
-    curLineWidth.value = width + 'rpx';
-};
-const curLineWidth = ref(props.lineWidth);
-const current = ref(0);
-watch(() => props.modelValue, (val) => {
-    current.value = props.tabs.findIndex(item => item.value === val);
-    if (props.isAutoWidth && current.value !== -1) {
-        setCurLineWidthset(current.value);
-    }
-}, { immediate: true });
-</script>
-<style lang="scss" scoped>
-.tab-fill {
-    display: inline-block;
-    white-space: nowrap;
-    font-size: 26rpx;
-    color: #999;
-    height: 54rpx;
-    line-height: 54rpx;
-    padding: 0 16rpx;
-    background: #F7F7F7;
-    border-radius: 8rpx;
-
-    &.checked {
-        background: #2A6D52;
-        color: #fff;
-    }
-}
-</style>
-
-<style lang="scss"></style>

+ 0 - 87
src/components/ut-tabs-items/ut-tabs-items.vue

@@ -1,87 +0,0 @@
-<template>
-    <view class="d-flex">
-        <scroll-view class="flex1 ov-hd scroll-x-info" scroll-x scroll-with-animation :scroll-into-view="intoView">
-            <template v-for="(item, index) in items" :key="index">
-                <view class="item-box p-rtv" :id="'xxdd' + item.id">
-                    <view class="item-in d-flex a-c j-c p-rtv" :class="{ checked: item.id === curChecked && item.id }" @click="clickItem(item)">
-                        <text>{{ item.varietyName }}</text>
-                        <view @click.stop="deleteRow(index, item)" class="checked-icon d-flex a-c j-c">
-                            <up-icon size="20rpx" color="#F56C6C" name="close"></up-icon>
-                        </view>
-                    </view>
-                </view>
-            </template>
-        </scroll-view>
-    </view>
-</template>
-<script setup name="ut-tabs-items">
-import { ref, getCurrentInstance, toRefs, watch } from 'vue';
-const props = defineProps({
-    items: {
-        type: Array,
-        default: () => []
-    },
-    checked: {
-        type: String,
-        default: ''
-    }
-})
-const curChecked = ref('');
-const intoView = ref('');
-const emit = defineEmits(['click', 'change', 'update:checked', 'delete']);
-const clickItem = (item) => {
-    emit('update:checked', item.id);
-    emit('change', item);
-    emit('click', item);
-};
-const deleteRow = (index, item) => {
-    emit('delete', { index, item});
-};
-watch(() => props.checked, (val) => {
-    console.log('val', val);
-    if (val) {
-        curChecked.value = val;
-        intoView.value = 'xxdd' + val;
-    }
-}, { immediate: true });
-</script>
-<style lang="scss" scoped>
-.item-box {
-    padding-right: 20rpx;
-    display: inline-block;
-    &::after {
-        content: '';
-        position: absolute;
-        left: 0;
-        bottom: 0;
-        right: 0;
-        height: 1rpx;
-        background-color: #F7F7F7;
-    }
-}
-
-.item-in {
-    height: 64rpx;
-    padding: 0 34rpx;
-    font-size: 30rpx;
-    font-size: 400;
-    background-color: #F7F7F7;
-    border-radius: 10rpx 10rpx 0 0;
-    border-width: 1rpx;
-    border-style: solid;
-    border-color: #F7F7F7;
-
-    &.checked {
-        border-color: $u-primary;
-        color: $u-primary;
-        background-color: #fff;
-    }
-}
-.checked-icon {
-    position: absolute;
-    right: 0;
-    top: 0;
-    width: 30rpx;
-    height: 30rpx;
-}
-</style>

+ 0 - 55
src/components/ut-tabs-pages/ut-tabs-pages.vue

@@ -1,55 +0,0 @@
-<template>
-    <view class="ut-tabs-page d-flex p-rtv ov-hd" :style="cStyle">
-        <template v-for="(item, index) in tabs" :key="index">
-            <view class="tabs-item-page d-flex a-c"  @click="clickItem(item.value)" :class="{ checked: checked === item.value }">
-                {{ item.label }}
-            </view>
-        </template>
-    </view>
-</template>
-<script setup name="ut-tabs-card">
-import { defineProps, ref, watch, onMounted, getCurrentInstance } from 'vue';
-const instance = getCurrentInstance();
-const emit = defineEmits(['change', 'update:modelValue'])
-const props = defineProps({
-    tabs: {
-        type: Array,
-        default: () => []
-    },
-    modelValue: {
-        type: String,
-        default: ''
-    },
-    cStyle: {
-        type: Object,
-        default: () => ({})
-    }
-})
-const checked = ref('')
-const clickItem = (val) => {
-    emit('update:modelValue', val)
-    emit('change', val)
-}
-
-onMounted(() => {
-    checked.value = props.modelValue
-})
-watch(() => props.modelValue, (val) => {
-    checked.value = val
-}, { immediate: true })
-</script>
-<style lang="scss" scoped>
-.tabs-item-page {
-    padding: 26rpx;
-    color: #999;
-    font-size: 30rpx;
-
-    &.checked {
-       border-radius: 8rpx 8rpx 0 0;
-       box-shadow: 6rpx -6rpx 16rpx 0px #E6E6E6, -6rpx -6rpx 16rpx 0px #E6E6E6;
-       color: $u-primary;
-       font-weight: 500;
-       transition: box-shadow 0.3s;
-    }
-}
-</style>

+ 0 - 77
src/components/ut-tabs-xpf/ut-tabs-xpf.vue

@@ -1,77 +0,0 @@
-<template>
-    <view class="ut-tabs-xpf d-flex a-c j-sb p-rtv" :style="cStyle">
-        <template v-for="(item, index) in tabs" :key="index">
-            <view class="tabs-item-x p-rtv" :style="{ color: checked === item.value ? color : '#999'  }" @click="clickItem(item.value)" :class="{ checked: checked === item.value }">
-                {{ item.label }}
-                <!-- 动画 -->
-                <up-transition :show="checked === item.value">
-                    <view class="arrow-dot" :style="dotStyle"></view>
-                </up-transition>
-            </view>
-        </template>
-
-    </view>
-</template>
-<script setup name="ut-tabs-xpf">
-import { defineProps, ref, watch, onMounted, getCurrentInstance } from 'vue';
-const instance = getCurrentInstance();
-const emit = defineEmits(['change', 'update:modelValue'])
-const props = defineProps({
-    tabs: {
-        type: Array,
-        default: () => []
-    },
-    modelValue: {
-        type: String,
-        default: ''
-    },
-    cStyle: {
-        type: Object,
-        default: () => ({})
-    },
-    dotStyle: {
-        type: Object,
-        default: () => ({})
-    },
-    color: {
-        type: String,
-        default: '#2D7357'
-    }
-})
-const checked = ref('')
-const clickItem = (val) => {
-    emit('update:modelValue', val)
-    emit('change', val)
-}
-
-onMounted(() => {
-    checked.value = props.modelValue
-})
-watch(() => props.modelValue, (val) => {
-    checked.value = val
-}, { immediate: true })
-</script>
-<style lang="scss" scoped>
-.tabs-item-x {
-    padding: 12rpx 19rpx 20rpx;
-    color: #999;
-    font-size: 30rpx;
-
-    &.checked {
-        color: #2D7357;
-        font-weight: 600;
-    }
-}
-
-.arrow-dot {
-    position: absolute;
-    left: 0;
-    right: 0;
-    bottom: -10rpx;
-    width: 20rpx;
-    height: 20rpx;
-    background: #E4F2ED;
-    transform: rotate(45deg);
-    margin: auto;
-}
-</style>

+ 0 - 68
src/components/ut-tabs/ut-tabs.vue

@@ -1,68 +0,0 @@
-<template>
-    <view class="tebs-wrap" :style="{ margin: margin }">
-        <template v-for="(item, index) in tabs" :key="index">
-            <view class="teb-item d-flex a-c j-c flex1 p-rtv" :class="{ checked: item.value === modelValue }" @click="clickItem(item)">
-                <view class="p-rtv">
-                    {{ item.name }}
-                    <view class="badge-box">
-                        <up-badge max="99" :value="item.badge" :offset="[0, 0]"></up-badge>
-                    </view>
-                </view>
-            </view>
-        </template>
-    </view>
-</template>
-<script setup>
-import { toRefs } from 'vue';
-const props = defineProps({
-  tabs: {
-    type: Array,
-    default: () => [],
-  },
-  modelValue: {
-    type: String,
-    default: "",
-  },
-  margin: {
-    type: String,
-    default: "24rpx",
-  },
-});
-const { tabs } = toRefs(props);
-const emit = defineEmits(["change", 'update:modelValue']);
-const clickItem = (item) => {
-  emit("update:modelValue", item.value);
-  emit("change", item.value);
-};
-</script>
-<style lang="scss" scoped>
-.tebs-wrap {
-  display: flex;
-  border: 1rpx solid $u-primary;
-  border-radius: 10rpx;
-  overflow: hidden;
-}
-
-.teb-item {
-  padding: 20rpx;
-  font-size: 36rpx;
-  font-weight: 400;
-  background-color: #E9F0ED;
-  color: $u-primary;
-
-  &.checked {
-    background-color: $u-primary;
-    color: #fff;
-  }
-}
-.badge-box {
-  position: absolute;
-  top: 0;
-  left: 100%;
-  bottom: 0;
-  margin: auto;
-  transform: translateX(10rpx);
-  display: flex;
-  align-items: center;
-}
-</style>

+ 0 - 36
src/components/ut-tag-dict/ut-tag-dict.vue

@@ -1,36 +0,0 @@
-<template>
-    <view class="ut-tag_view" :style="getTagClassStyle(options, value)">{{ selectDictLabel(options, value) }}</view>
-</template>
-<script setup>
-const props = defineProps({
-    options: {
-        type: Array,
-        default: () => []
-    },
-    value: {
-        type: [String, Number],
-        default: ''
-    }
-});
-// 获取自定中的样式elTagClass方法
-const getTagClassStyle = (options, value) => {
-    const option = options.find(item => item.value === value);
-    if (option) {
-        return option?.elTagClass ? JSON.parse(option?.elTagClass) : null;
-    }
-    return {};
-};
-
-</script>
-<style lang="scss" scoped>
-.ut-tag_view {
-    display: inline-block;
-    padding: 0rpx 16rpx;
-    height: 34rpx;
-    line-height: 34rpx;
-    border-radius: 8rpx;
-    background-color: #f5f5f5;
-    color: #333;
-    font-size: 24rpx;
-}
-</style>

+ 0 - 44
src/components/ut-title/ut-title.vue

@@ -1,44 +0,0 @@
-<template>
-  <view class="ut-title d-flex a-c">
-    <view class="ut-subicon mr-20" :style="{ width: lineWidth, background: lineColor, height: fontSize }"></view>
-    <slot>
-      <view class="f-s-34 c-light-black f-w-5 ut-title-text" :style="{ fontSize: fontSize, color: color }">{{ props.text
-        }}</view>
-    </slot>
-  </view>
-</template>
-<script setup name="ut-title">
-const props = defineProps({
-  text: {
-    type: String,
-    default: ''
-  },
-  fontSize: {
-    type: String,
-    default: '34rpx'
-  },
-  color: {
-    type: String,
-    default: '#333'
-  },
-  lineColor: {
-    type: String,
-    default: '#2a6d52'
-  },
-  lineWidth: {
-    type: String,
-    default: '4rpx'
-  }
-});
-
-</script>
-<style lang="scss" scoped>
-.ut-subicon {
-  width: 4rpx;
-  background-color: $u-primary;
-}
-
-.ut-title-text {
-  line-height: 1.2;
-}
-</style>

+ 0 - 99
src/components/ut-upload-image/ut-upload-image.vue

@@ -1,99 +0,0 @@
-<template>
-    <view v-if="preview" class="ut-upload-image p-rtv" @click="previewImg" :style="{ width, height }">
-        <up-image :src="value" mode="aspectFit" :width="width" :height="height"></up-image>
-    </view>
-    <view v-else class="ut-upload-image p-rtv" @click="choose" :style="{ width, height }">
-        <up-image v-if="value" :src="value" mode="aspectFit" :width="width" :height="height"></up-image>
-        <image v-else class="ut-cover-image" :src="cover" mode="widthFix" />
-    </view>
-</template>
-<script setup>
-import { ref, watch } from 'vue';
-import upload from '@/utils/upload';
-
-const props = defineProps({
-    cover: {
-        type: String,
-        default: ''
-    },
-    width: {
-        type: String,
-        default: '326rpx'
-    },
-    height: {
-        type: String,
-        default: '200rpx'
-    },
-    uploadText: {
-        type: String,
-        default: '上传图片'
-    },
-    modelValue: {
-        type: String,
-        default: ''
-    },
-    disabled: {
-        type: Boolean,
-        default: false
-    },
-    preview: {
-        type: Boolean,
-        default: false
-    }
-});
-const emit = defineEmits(['update:modelValue', '']);
-const value = ref();
-// 监听modelValue的变化
-watch(() => props.modelValue, (val) => {
-    value.value = val;
-}, { immediate: true });
-const choose = () => {
-    if (props.disabled) {
-        return;
-    }
-    uni.chooseImage({
-        count: 1,
-        sizeType: ['compressed'],
-        sourceType: ['album', 'camera'],
-        success: (res) => {
-            const tempFilePaths = res.tempFilePaths;
-            upload({
-                filePath: tempFilePaths[0],
-                url: '/resource/oss/upload'
-            }).then((res) => {
-                console.log(res);
-                emit('update:modelValue', res.data.url);
-                emit('change', res.data.url);
-            });
-        }
-    });
-};
-const previewImg = () => {
-	if (value.value) {
-		uni.previewImage({
-		    urls: [value.value]
-		});
-	}
-};
-</script>
-<style lang="scss" scoped>
-.ut-upload-image {
-    border-radius: 16rpx;
-    border: 1rpx dashed #ccc;
-}
-
-.ut-cover-image {
-    position: absolute;
-    left: 0;
-    top: 0;
-    width: 100%;
-    height: 100%;
-}
-.ut-image {
-    position: absolute;
-    left: 0;
-    top: 0;
-    width: 100%;
-    height: 100%;
-}
-</style>

+ 0 - 139
src/components/ut-upload/ut-upload.vue

@@ -1,139 +0,0 @@
-<template>
-	<u-upload :width="width" :height="height" :fileList="fileList" :accept="accept" :uploadIcon="uploadIcon"
-		:uploadText="uploadText" @afterRead="afterRead" @delete="deletePic" :multiple="multiple"
-		:maxCount="maxCount">
-	</u-upload>
-</template>
-
-<script setup lang="ts">
-import upload from '@/utils/upload';
-
-interface FileItem {
-    url: string;
-}
-
-interface UploadEvent {
-    file: Array<{ url: string }>;
-}
-
-interface DeleteEvent {
-    index: number;
-}
-
-interface Props {
-    modelValue: string | string[] | Record<string, any> | null;
-    maxCount: number;
-    width: string;
-    height: string;
-    multiple: boolean;
-    uploadText: string;
-    uploadIcon: string;
-    accept: string;
-    isArr: boolean;
-    keyUrl: string;
-    isObject: boolean;
-}
-
-const props = withDefaults(defineProps<Props>(), {
-    modelValue: () => [],
-    maxCount: 1,
-    width: '200rpx',
-    height: '200rpx',
-    multiple: true,
-    uploadText: '点击上传',
-    uploadIcon: 'plus',
-    accept: 'image',
-    isArr: false,
-    keyUrl: '',
-    isObject: false
-});
-
-const emit = defineEmits<{
-    change: [value: any];
-    'update:modelValue': [value: any];
-}>();
-
-const fileList = ref<FileItem[]>([]);
-
-watch(() => props.modelValue, (ov) => {
-    if (ov) {
-        if (props.isObject) {
-            fileList.value = [ov as FileItem];
-        } else if (props.isArr) {
-            if (props.keyUrl) {
-                fileList.value = (ov as any[]).map(url => ({ url: url[props.keyUrl] }));
-            } else {
-                fileList.value = (ov as string[]).map(url => ({ url }));
-            }
-        } else {
-            const list = (ov as string).split(',');
-            fileList.value = list.map(item => ({
-                url: item
-            }));
-        }
-    }
-});
-
-const deletePic = (event: DeleteEvent) => {
-    fileList.value.splice(event.index, 1);
-    const urls = fileList.value.map(({ url }) => url);
-    const imgs = urls.toString();
-    
-    if (props.isObject) {
-        emit('update:modelValue', null);
-        emit('change', null);
-    } else if (props.isArr) {
-        if (props.keyUrl) {
-            emit('update:modelValue', fileList.value.map(({ url }) => ({ [props.keyUrl]: url })));
-            emit('change', fileList.value.map(({ url }) => ({ [props.keyUrl]: url })));
-        } else {
-            emit('update:modelValue', urls);
-            emit('change', urls);
-        }
-    } else {
-        emit('update:modelValue', imgs);
-        emit('change', imgs);
-    }
-};
-
-const afterRead = async (event: UploadEvent) => {
-    const files = event.file;
-    const promises = files.map(({ url }) => upload({
-        filePath: url,
-        url: '/resource/oss/upload'
-    }));
-    
-    try {
-        const res = await Promise.all(promises);
-        const list: FileItem[] = [];
-        res.forEach(({ code, data }) => {
-            if (code === 200) {
-                list.push(data);
-            }
-        });
-        
-        const urls = fileList.value.concat(list);
-        const imgs = urls.map(({ url }) => url).toString();
-        
-        if (props.isObject) {
-            emit('update:modelValue', urls[0]);
-            emit('change', urls[0]);
-        } else if (props.isArr) {
-            if (props.keyUrl) {
-                emit('update:modelValue', urls.map(({ url }) => ({ [props.keyUrl]: url })));
-                emit('change', urls.map(({ url }) => ({ [props.keyUrl]: url })));
-            } else {
-                emit('update:modelValue', urls.map(({ url }) => url));
-                emit('change', urls.map(({ url }) => url));
-            }
-        } else {
-            emit('update:modelValue', imgs);
-            emit('change', imgs);
-        }
-    } catch (error) {
-        console.error('Upload failed:', error);
-    }
-};
-</script>
-
-<style></style>

+ 4 - 2
src/main.ts

@@ -5,11 +5,13 @@ import 'uno.css';
 import { selectDictLabel, selectDictLabels } from './utils/ruoyi';
 import { useDict } from '@/utils/dict';
 import uviewPlus from 'uview-plus';
-
+import { navigateBackOrHome, showToast } from '@/utils/common';
 export function createApp() {
     const app = createSSRApp(App);
     app.use(Pinia.createPinia());
-    app.use(uviewPlus)
+    app.use(uviewPlus);
+    app.config.globalProperties.navigateBackOrHome = navigateBackOrHome;
+    app.config.globalProperties.showToast = showToast;
     app.config.globalProperties.selectDictLabel = selectDictLabel;
     app.config.globalProperties.selectDictLabels = selectDictLabels;
     app.config.globalProperties.useDict = useDict;

+ 80 - 38
src/pages.json

@@ -1,39 +1,81 @@
 {
-	"easycom": {
-		"autoscan": true,
-		"custom": {
-			"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
-			"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
-			"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue",
-			"^ut-(.*)": "@/src/components/ut-$1/ut-$1.vue",
-			"^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue"
-		}
-	},
-	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
-		{
-			"path": "pages/index/index",
-			"style": {
-				"navigationBarTitleText": "首页"
-			}
-		},
-		{
-			"path": "pages/login/login",
-			"style": {
-				"navigationBarTitleText": "用户登录",
-				"navigationStyle": "default",
-				"navigationBarBackgroundColor": "#667eea",
-				"navigationBarTextStyle": "white"
-			}
-		}
-	],
-	"globalStyle": {
-		"navigationStyle": "custom",
-		"navigationBarTextStyle": "black",
-		"navigationBarTitleText": "uni-app",
-		"navigationBarBackgroundColor": "#F8F8F8",
-		"backgroundColor": "#F8F8F8",
-		"rpxCalcMaxDeviceWidth": 1920,
-		"rpxCalcBaseDeviceWidth": 375,
-		"rpxCalcIncludeWidth": 750
-	}
-}
+    "easycom": {
+        "autoscan": true,
+        "custom": {
+            "^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
+            "^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
+            "^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue",
+            "^ut-(.*)": "@/src/components/ut-$1/ut-$1.vue",
+            "^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue"
+        }
+    },
+    "pages": [
+        //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+        {
+            "path": "pages/index/index",
+            "style": {
+                "navigationBarTitleText": "首页"
+            }
+        },
+        {
+            "path": "pages/login/login",
+            "style": {
+                "navigationBarTitleText": "用户登录",
+                "navigationStyle": "default",
+                "navigationBarBackgroundColor": "#667eea",
+                "navigationBarTextStyle": "white"
+            }
+        },
+        {
+            "path": "pages/plant/index",
+            "style": {
+                "navigationBarTitleText": "种植端",
+            }
+        },
+        {
+            "path": "pages/production/index",
+            "style": {
+                "navigationBarTitleText": "生产端",
+            }
+        }
+    ],
+    "subPackages": [
+        {
+            "root": "plant",
+            "pages": [
+                {
+                    "path": "index/index",
+                    "style": {
+                        "navigationBarTitleText": "企业信息",
+                        "disableScroll": true,
+                        "enablePullDownRefresh": false
+                    }
+                }
+            ]
+        }
+    ],
+    "tabBar": {
+        "custom": true,
+        "list": [
+            {
+                "pagePath": "pages/index/index"
+            },
+            {
+                "pagePath": "pages/plant/index"
+            },
+            {
+                "pagePath": "pages/production/index"
+            }
+        ]
+    },
+    "globalStyle": {
+        "navigationStyle": "custom",
+        "navigationBarTextStyle": "black",
+        "navigationBarTitleText": "uni-app",
+        "navigationBarBackgroundColor": "#F8F8F8",
+        "backgroundColor": "#F8F8F8",
+        "rpxCalcMaxDeviceWidth": 1920,
+        "rpxCalcBaseDeviceWidth": 375,
+        "rpxCalcIncludeWidth": 750
+    }
+}

+ 27 - 35
src/pages/index/index.vue

@@ -4,10 +4,7 @@
             <!-- 用户信息区域 -->
             <view class="user-info-section">
                 <view v-if="userInfo" class="user-card">
-                    <image 
-                        :src="userInfo.avatar || '/static/logo.png'" 
-                        class="user-avatar"
-                    />
+                    <image :src="userInfo.avatar || '/static/logo.png'" class="user-avatar" />
                     <view class="user-details">
                         <text class="user-name">{{ userInfo.nickname || userInfo.username }}</text>
                         <text class="user-roles">{{ userInfo.roles.join(', ') }}</text>
@@ -20,22 +17,16 @@
                 </view>
             </view>
         </template>
-        
         <view class="content">
             <image class="logo" src="/static/logo.png" />
             <view class="text-area">
                 <text class="title">{{ title }}</text>
             </view>
             <view class="mb-80">{{ selectDictLabel(class_type, 1) }}</view>
-            <view class="bg-blue-500 text-white p-4 rounded">Hello UnoCSS!</view>
-         
-            <template v-for="(item, index) in list" :key="index">
-                <up-button 
-                    text="渐变色按钮" 
-                    color="linear-gradient(to right, rgb(66, 83, 216), rgb(213, 51, 186))"
-                />
-            </template>
-        </view> 
+            <view class="bg-blue-500 c-primary p-4 rounded">Hello UnoCSS!</view>
+            <up-button @click="$u.route({ type: 'switchTab', url: '/pages/plant/index' })" text="plant" color="linear-gradient(to right, rgb(66, 83, 216), rgb(213, 51, 186))" />
+            <up-button @click="$u.route({ type: 'switchTab', url: '/pages/production/index' })" text="production" color="linear-gradient(to right, rgb(66, 83, 216), rgb(213, 51, 186))" />
+        </view>
     </z-paging>
 </template>
 <script setup lang="ts">
@@ -45,8 +36,8 @@ import { useUserStore } from '@/store/modules/user';
 import { checkAuth, logoutAndRedirect } from '@/utils/routeGuard';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { class_type } = toRefs<any>(proxy?.useDict("class_type"));
-const title = ref("Hello");
+const { class_type } = toRefs<any>(proxy?.useDict('class_type'));
+const title = ref('Hello');
 const paging = ref<any>(null);
 
 // Store
@@ -61,10 +52,10 @@ console.log(import.meta.env);
 onMounted(async () => {
     // 检查登录状态
     await checkAuth({ requireAuth: false });
-    
+
     uni.showToast({
-        title: "Hello World!",
-        icon: "none",
+        title: 'Hello World!',
+        icon: 'none',
         duration: 2000,
     });
 });
@@ -73,7 +64,7 @@ const list = ref<any[]>([]);
 
 const queryList = (pageNo: number, pageSize: number) => {
     console.log(`请求第${pageNo}页,每页${pageSize}条数据`);
-    
+
     // 这里的pageNo和pageSize会自动计算好,直接传给服务器即可
     // 这里的请求只是演示,请替换成自己的项目的网络请求,并在网络请求回调中通过paging.value.complete(请求回来的数组)将请求结果传给z-paging
     setTimeout(() => {
@@ -88,10 +79,10 @@ const queryList = (pageNo: number, pageSize: number) => {
 const onRefresh = () => {
     try {
         // 这里可以执行一些刷新操作,比如重新请求数据
-        console.log("页面刷新");
+        console.log('页面刷新');
         paging.value.refresh(); // 调用z-paging的refresh方法
     } catch (error) {
-        console.error("刷新失败", error);
+        console.error('刷新失败', error);
     }
 };
 
@@ -100,7 +91,7 @@ const onRefresh = () => {
  */
 const goToLogin = (): void => {
     uni.navigateTo({
-        url: '/pages/login/login'
+        url: '/pages/login/login',
     });
 };
 
@@ -117,26 +108,27 @@ const handleLogout = async (): Promise<void> => {
                     await logoutAndRedirect();
                     uni.showToast({
                         title: '已退出登录',
-                        icon: 'success'
+                        icon: 'success',
                     });
                 }
-            }
+            },
         });
     } catch (error) {
         console.error('退出登录失败:', error);
         uni.showToast({
             title: '退出失败',
-            icon: 'error'
+            icon: 'error',
         });
     }
 };
 </script>
 
 <style lang="scss" scoped>
+@import '@/assets/styles/theme.scss';
 .user-info-section {
     padding: 20rpx;
     background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-    
+
     .user-card {
         display: flex;
         align-items: center;
@@ -144,17 +136,17 @@ const handleLogout = async (): Promise<void> => {
         background: rgba(255, 255, 255, 0.95);
         border-radius: 12rpx;
         box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
-        
+
         .user-avatar {
             width: 80rpx;
             height: 80rpx;
             border-radius: 50%;
             margin-right: 20rpx;
         }
-        
+
         .user-details {
             flex: 1;
-            
+
             .user-name {
                 display: block;
                 font-size: 32rpx;
@@ -162,14 +154,14 @@ const handleLogout = async (): Promise<void> => {
                 color: #333;
                 margin-bottom: 8rpx;
             }
-            
+
             .user-roles {
                 display: block;
                 font-size: 24rpx;
                 color: #666;
             }
         }
-        
+
         .logout-btn {
             padding: 12rpx 24rpx;
             background: #f56c6c;
@@ -179,7 +171,7 @@ const handleLogout = async (): Promise<void> => {
             font-size: 24rpx;
         }
     }
-    
+
     .login-prompt {
         display: flex;
         align-items: center;
@@ -187,12 +179,12 @@ const handleLogout = async (): Promise<void> => {
         padding: 20rpx;
         background: rgba(255, 255, 255, 0.95);
         border-radius: 12rpx;
-        
+
         .prompt-text {
             font-size: 28rpx;
             color: #666;
         }
-        
+
         .login-btn {
             padding: 12rpx 24rpx;
             background: #667eea;

+ 14 - 0
src/pages/plant/index.vue

@@ -0,0 +1,14 @@
+<template>
+    <z-paging ref="paging" v-model="list" @query="queryList" @onRefresh="onRefresh">
+        <template #top>
+            <up-navbar title="种植端首页" @leftClick="navigateBackOrHome()" :fixed="false"></up-navbar>
+        </template>
+        <view class="">种植端首页</view>
+    </z-paging>
+</template>
+<script setup lang="ts">
+import { useClientRequest } from '@/utils/request';
+setTimeout(() => {
+    useClientRequest.get('/time');
+}, 2000);
+</script>

+ 12 - 0
src/pages/production/index.vue

@@ -0,0 +1,12 @@
+<template>
+    <template #top>
+        <up-navbar title="生产端首页" @leftClick="navigateBackOrHome()" :fixed="false"></up-navbar>
+    </template>
+    <view class="">生产端首页</view>
+</template>
+<script setup lang="ts">
+import { useClientRequest } from '@/utils/request';
+setTimeout(() => {
+    useClientRequest.get('/time');
+}, 2000);
+</script>

+ 10 - 0
src/plant/index/index.vue

@@ -0,0 +1,10 @@
+<template>
+    <view class="c-primary">种植端</view>
+</template>
+<script setup lang="ts">
+import { useClientRequest } from '@/utils/request';
+setTimeout(() => {
+    useClientRequest.get('/time');
+}, 2000);
+</script>
+<style></style>

+ 10 - 0
src/production/index/index.vue

@@ -0,0 +1,10 @@
+<template>
+    <view>生产端</view>
+</template>
+<script setup lang="ts">
+import { useClientRequest } from '@/utils/request';
+setTimeout(() => {
+    useClientRequest.get('/time');
+}, 2000);
+</script>
+<style></style>

+ 3 - 1
src/uni.scss

@@ -14,7 +14,9 @@
 
 /* 颜色变量 */
 @import 'uview-plus/theme.scss';
-
+@import '@/assets/styles/public.scss';
+@import '@/assets/styles/common.scss';
+@import '@/assets/styles/uview-plus.scss';
 /* 行为相关颜色 */
 $uni-color-primary: #007aff;
 $uni-color-success: #4cd964;

+ 89 - 60
src/utils/request.ts

@@ -1,60 +1,89 @@
-// uniapp封装的请求方法
-let timeout = 60 * 1000;
-// 获取全局请求头方法
-const getHeader = () => {
-    let header = {
-        'Content-Type': 'application/json',
-        Authorization: uni.getStorageSync('token') || '',
-    };
-    return header;
-};
-// 获取host地址
-export const request = ({ url, method = 'GET', data = {}, header = null }: any) => {
-    const VITE_API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000';
-    return new Promise((resolve, reject) => {
-        uni.request({
-            url: VITE_API_BASE_URL + url,
-            method,
-            data,
-            timeout: timeout,
-            header: header || getHeader(),
-            success: (res: any) => {
-                resolve(res.data);
-            },
-            fail: (err) => {
-                reject(err);
-            },
-        });
-    });
-};
-export const useClientRequest = {
-    post: (url: string, data?: any) => {
-        return request({
-            url,
-            method: 'POST',
-            data,
-        });
-    },
-    get: (url: string, data?: any) => {
-        return request({
-            url,
-            method: 'GET',
-            data,
-        });
-    },
-    put: (url: string, data?: any) => {
-        return request({
-            url,
-            method: 'PUT',
-            data,
-        });
-    },
-    delete: (url: string, data?: any) => {
-        return request({
-            url,
-            method: 'DELETE',
-            data,
-        });
-    },
-};
-
+// uniapp封装的请求方法
+let timeout = 60 * 1000;
+
+// 获取当前应该使用的 base URL
+const getBaseUrl = (): string => {
+    try {
+        // 获取当前页面栈
+        const pages = getCurrentPages();
+        if (pages.length === 0) {
+            // 默认使用 VITE_API_BASE_URL
+            return import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000';
+        }
+        // 获取当前页面路由
+        const currentPage = pages[pages.length - 1];
+        const route = currentPage.route || '';
+        console.log(route, 'route');
+
+        // 判断路由路径
+        if (route.includes('plant/')) {
+            return import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000';
+        } else if (route.includes('production/')) {
+            return import.meta.env.VITE_API_OTHER_BASE_URL || 'http://localhost:3000';
+        }
+
+        // 默认情况
+        return import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000';
+    } catch (error) {
+        // 出错时使用默认值
+        return import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000';
+    }
+};
+
+// 获取全局请求头方法
+const getHeader = () => {
+    let header = {
+        'Content-Type': 'application/json',
+        Authorization: uni.getStorageSync('token') || '',
+    };
+    return header;
+};
+// 获取host地址
+export const request = ({ url, method = 'GET', data = {}, header = null }: any) => {
+    const baseUrl = getBaseUrl();
+    return new Promise((resolve, reject) => {
+        uni.request({
+            url: baseUrl + url,
+            method,
+            data,
+            timeout: timeout,
+            header: header || getHeader(),
+            success: (res: any) => {
+                resolve(res.data);
+            },
+            fail: (err) => {
+                reject(err);
+            },
+        });
+    });
+};
+export const useClientRequest = {
+    post: (url: string, data?: any) => {
+        return request({
+            url,
+            method: 'POST',
+            data,
+        });
+    },
+    get: (url: string, data?: any) => {
+        return request({
+            url,
+            method: 'GET',
+            data,
+        });
+    },
+    put: (url: string, data?: any) => {
+        return request({
+            url,
+            method: 'PUT',
+            data,
+        });
+    },
+    delete: (url: string, data?: any) => {
+        return request({
+            url,
+            method: 'DELETE',
+            data,
+        });
+    },
+};

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
stats.html


+ 147 - 6
unocss.config.js

@@ -30,17 +30,158 @@ export default defineConfig({
     shortcuts: [
         {
             center: 'flex justify-center items-center',
+            'p-rtv': 'position: relative',
+            'text-indent-2': 'text-indent: 2em',
+            'w-s-no': 'white-space: nowrap',
         },
     ],
     rules: [
+        [/^mg-([\.\d]+)$/, ([_, num]) => ({ margin: `${num}rpx` })],
+        [/^pd-([\.\d]+)$/, ([_, num]) => ({ padding: `${num}rpx` })],
+        [/^bg-([\w#-]+)$/, ([_, color]) => ({ 'background-color': color })],
+        [/^c-([\w#-]+)$/, ([_, color]) => ({ color: color })],
+        // bc-#fafafa
+        [/^bc-([\w#-]+)$/, ([_, color]) => ({ 'border-color': color })],
+        // 下边距
+        [/^mb-([\.\d]+)$/, ([_, num]) => ({ 'margin-bottom': `${num}rpx` })],
+        // 上边距
+        [/^mt-([\.\d]+)$/, ([_, num]) => ({ 'margin-top': `${num}rpx` })],
+        // 左边距
+        [/^ml-([\.\d]+)$/, ([_, num]) => ({ 'margin-left': `${num}rpx` })],
+        // 右边距
+        [/^mr-([\.\d]+)$/, ([_, num]) => ({ 'margin-right': `${num}rpx` })],
+        // 上padding
+        [/^pt-([\.\d]+)$/, ([_, num]) => ({ 'padding-top': `${num}rpx` })],
+        // 下padding
+        [/^pb-([\.\d]+)$/, ([_, num]) => ({ 'padding-bottom': `${num}rpx` })],
+        // pd3-40-0-20
+        // padding
         [
-            'p-safe',
-            {
-                padding: 'env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)',
-            },
+            /^pd3-([\.\d]+)-([\.\d]+)-([\.\d]+)$/,
+            ([_, top, right, bottom]) => ({
+                padding: `${top}rpx ${right}rpx ${bottom}rpx`,
+            }),
+        ],
+        // pd2-40-20
+        [
+            /^pd2-([\.\d]+)-([\.\d]+)$/,
+            ([_, top, right]) => ({
+                padding: `${top}rpx ${right}rpx`,
+            }),
+        ],
+        // pd4-40-20-10-5
+        [
+            /^pd4-([\.\d]+)-([\.\d]+)-([\.\d]+)-([\.\d]+)$/,
+            ([_, top, right, bottom, left]) => ({
+                padding: `${top}rpx ${right}rpx ${bottom}rpx ${left}rpx`,
+            }),
+        ],
+        // mg2-40-20
+        [
+            /^mg2-([\.\d]+)-([\.\d]+)$/,
+            ([_, top, right]) => ({
+                margin: `${top}rpx ${right}rpx`,
+            }),
+        ],
+        // mg3-40-20-10
+        [
+            /^mg3-([\.\d]+)-([\.\d]+)-([\.\d]+)$/,
+            ([_, top, right, bottom]) => ({
+                margin: `${top}rpx ${right}rpx ${bottom}rpx`,
+            }),
+        ],
+        // mg4-40-20-10-5
+        [
+            /^mg4-([\.\d]+)-([\.\d]+)-([\.\d]+)-([\.\d]+)$/,
+            ([_, top, right, bottom, left]) => ({
+                margin: `${top}rpx ${right}rpx ${bottom}rpx ${left}rpx`,
+            }),
+        ],
+        // 左padding
+        [/^pl-([\.\d]+)$/, ([_, num]) => ({ 'padding-left': `${num}rpx` })],
+        // 右padding
+        [/^pr-([\.\d]+)$/, ([_, num]) => ({ 'padding-right': `${num}rpx` })],
+        // 字体大小
+        [/^f-s-([\.\d]+)$/, ([_, num]) => ({ 'font-size': `${num}rpx` })],
+        // 字体加粗
+        [/^f-w-([\.\d]+)$/, ([_, num]) => ({ 'font-weight': `${num > 100 ? num : num * 100}` })],
+        // 宽
+        [/^w-([\.\d]+)$/, ([_, num]) => ({ width: `${num}rpx` })],
+        // 高
+        [/^h-([\.\d]+)$/, ([_, num]) => ({ height: `${num}rpx` })],
+        // 带透明度的可传入颜色计算rgba值加背景
+        // 行高
+        [/^lh-([\.\d]+)$/, ([_, num]) => ({ 'line-height': `${num}` })],
+        // 黑色透明度
+        [
+            /^bg-black-([\.\d]+)$/,
+            ([_, num]) => ({
+                'background-color': `rgba(0, 0, 0, ${num})`,
+            }),
+        ],
+        // 透明度
+        [
+            /^opacity-([\.\d]+)$/,
+            ([_, num]) => ({
+                opacity: `${num}`,
+            }),
+        ],
+        // z-index
+        [
+            /^z-index-([\.\d]+)$/,
+            ([_, num]) => ({
+                'z-index': `${num}`,
+            }),
+        ],
+        // 百分比高度
+        [/^h-([\.\d]+)%$/, ([_, num]) => ({ height: `${num}%` })],
+        [
+            /^wrapper-([\.\d]+)-([\.\d]+)$/,
+            ([_, maxWidth, minWidth]) => ({
+                'max-width': `${maxWidth}rpx`,
+                'min-width': `${minWidth}rpx`,
+                margin: '0 auto',
+                padding: '0 20px',
+            }),
+        ],
+        // 最大宽
+        [/^max-w-([\.\d]+)$/, ([_, num]) => ({ 'max-width': `${num}rpx` })],
+        // 最小宽
+        [/^min-w-([\.\d]+)$/, ([_, num]) => ({ 'min-width': `${num}rpx` })],
+        // 最大高
+        [/^max-h-([\.\d]+)$/, ([_, num]) => ({ 'max-height': `${num}rpx` })],
+        // 最小高
+        [/^min-h-([\.\d]+)$/, ([_, num]) => ({ 'min-height': `${num}rpx` })],
+        // 栅格 30份
+        [/^hcol-([\.\d]+)$/, ([_, num]) => ({ width: `${(+num / 30) * 100}%` })],
+        // border-radius-8
+        [
+            /^border-radius-([\.\d]+)$/,
+            ([_, num]) => ({
+                'border-radius': `${num}rpx`,
+            }),
+        ],
+        // border-w-4
+        [
+            /^border-w-([\.\d]+)$/,
+            ([_, num]) => ({
+                'border-width': `${num}rpx`,
+            }),
+        ],
+        // 旋转度数
+        [
+            /^rotate-([\-\.\d]+)$/,
+            ([_, num]) => ({
+                transform: `rotate(${num}deg)`,
+            }),
+        ],
+        // gap-20
+        [
+            /^gap-([\.\d]+)$/,
+            ([_, num]) => ({
+                gap: `${num}rpx`,
+            }),
         ],
-        ['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],
-        ['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],
     ],
     theme: {
         colors: {

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است