Procházet zdrojové kódy

添加场地列表UI

lex před 2 roky
rodič
revize
544faf7512

binární
src/common/images/icon-music.png


binární
src/common/images/icon-preview-close.png


binární
src/common/images/icon-preview-download.png


binární
src/common/images/icon-setting.png


binární
src/common/images/icon-student-default.png


binární
src/common/images/icon-teacher-default.png


+ 15 - 0
src/components/m-image-preview/index.module.less

@@ -0,0 +1,15 @@
+.imagePreview {
+  --van-image-preview-close-icon-size: 32px;
+  --van-image-preview-index-line-height: 32px;
+
+  :global {
+    .van-image-preview__cover {
+      top: calc(var(--van-padding-md) + constant(safe-area-inset-bottom));
+      top: calc(var(--van-padding-md) + env(safe-area-inset-bottom));
+      right: var(--van-image-preview-close-icon-margin);
+      left: initial;
+      font-size: 32px;
+    }
+
+  }
+}

+ 114 - 0
src/components/m-image-preview/index.tsx

@@ -0,0 +1,114 @@
+import {
+  ImagePreview,
+  Image,
+  Icon,
+  showLoadingToast,
+  showSuccessToast,
+  showFailToast
+} from 'vant';
+import { PropType, defineComponent, onMounted, reactive, watch } from 'vue';
+import styles from './index.module.less';
+import iconPreviewClose from '@/common/images/icon-preview-close.png';
+import iconPreviewDownload from '@/common/images/icon-preview-download.png';
+import { promisefiyPostMessage } from '@/helpers/native-message';
+
+export default defineComponent({
+  name: 'm-image-preview',
+  props: {
+    show: {
+      tyep: Boolean,
+      default: false
+    },
+    images: {
+      type: Array as PropType<string[]>,
+      default: () => []
+    },
+    showIndex: {
+      type: Boolean,
+      default: true
+    },
+    startPosition: {
+      type: Number,
+      default: 0
+    },
+    loop: {
+      type: Boolean,
+      default: false
+    },
+    closeIconPosition: {
+      type: String as PropType<
+        'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
+      >,
+      default: 'top-left'
+    },
+    showDownload: {
+      type: Boolean,
+      default: true
+    }
+  },
+  emits: ['update:show'],
+  setup(props, { emit }) {
+    const forms = reactive({
+      show: false,
+      saveLoading: false
+    });
+
+    const onSave = async (img: string) => {
+      if (forms.saveLoading) return;
+      forms.saveLoading = true;
+      showLoadingToast({ message: '下载中...', forbidClick: true });
+      try {
+        const res = await promisefiyPostMessage({
+          api: 'savePicture',
+          content: {
+            img
+          }
+        });
+        if (res?.content?.status === 'success') {
+          showSuccessToast('保存成功');
+        } else {
+          showFailToast('保存失败');
+        }
+      } catch {
+        //
+      }
+      forms.saveLoading = false;
+    };
+
+    onMounted(() => {
+      forms.show = props.show;
+    });
+
+    watch(
+      () => props.show,
+      () => {
+        forms.show = props.show;
+      }
+    );
+    return () => (
+      <ImagePreview
+        class={styles.imagePreview}
+        v-model:show={forms.show}
+        images={props.images}
+        showIndex={props.showIndex}
+        startPosition={props.startPosition}
+        maxZoom={3}
+        minZoom={'1/3'}
+        loop={props.loop}
+        onClose={() => emit('update:show', forms.show)}
+        closeable
+        closeIcon={iconPreviewClose}
+        closeIconPosition="top-left">
+        {{
+          cover: () =>
+            props.showDownload && (
+              <Icon name={iconPreviewDownload} onClick={onSave} />
+            ),
+          image: ({ src }: any) => (
+            <Image class="van-image-preview__image" src={src} />
+          )
+        }}
+      </ImagePreview>
+    );
+  }
+});

+ 17 - 14
src/components/m-sticky/index.tsx

@@ -1,4 +1,3 @@
-import { Sticky } from 'vant';
 import {
   PropType,
   defineComponent,
@@ -66,23 +65,27 @@ export default defineComponent({
 
       nextTick(() => {
         // 为了处理刚开始头部高度为0的情况
-        const { height } = useRect(divRef.value);
-        __initHeight(height);
-
-        setTimeout(() => {
+        if (divRef.value) {
           const { height } = useRect(divRef.value);
-          // 判断获取的高度是否一致,如果一致则不做处理
-          if (height === forms.heightV) return;
           __initHeight(height);
-        }, 200);
 
-        // 为了处理头部第一次获取高度不对的问题
-        setTimeout(() => {
-          const { height } = useRect(div2Ref.value);
-          if (height !== forms.heightV && props.position === 'top') {
+          setTimeout(() => {
+            const { height } = useRect(divRef.value);
+            // 判断获取的高度是否一致,如果一致则不做处理
+            if (height === forms.heightV) return;
             __initHeight(height);
-          }
-        }, 1000);
+          }, 200);
+        }
+
+        // 为了处理头部第一次获取高度不对的问题
+        if (div2Ref.value) {
+          setTimeout(() => {
+            const { height } = useRect(div2Ref.value);
+            if (height !== forms.heightV && props.position === 'top') {
+              __initHeight(height);
+            }
+          }, 1000);
+        }
       });
     });
 

+ 8 - 0
src/router/routes-common.ts

@@ -28,5 +28,13 @@ export default [
       }
     ]
   },
+  {
+    path: '/site-management',
+    name: 'site-management',
+    component: () => import('@/views/site-management/index'),
+    meta: {
+      title: '场地管理'
+    }
+  },
   ...rootRouter
 ];

+ 14 - 0
src/styles/index.less

@@ -39,3 +39,17 @@ body {
   padding-bottom: calc(20px + constant(safe-area-inset-bottom));
   padding-bottom: calc(20px + env(safe-area-inset-bottom));
 }
+
+
+.btnGroupPopup {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 18px 13px;
+
+  .van-button {
+    font-weight: 400;
+    width: 168px;
+  }
+
+}

+ 0 - 1
src/views/layout/auth.tsx

@@ -53,7 +53,6 @@ export default defineComponent({
           });
           setLogin(res.data);
         } catch (e: any) {
-          // console.log(e, 'e')
           const message = e.message;
           if (
             message.indexOf('5000') === -1 &&

binární
src/views/school-register/images/banner-popup.png


binární
src/views/school-register/images/banner.png


+ 72 - 1
src/views/school-register/index.module.less

@@ -2,12 +2,43 @@
   --van-cell-font-size: 16px;
 
   .banner {
+    position: relative;
     width: 100%;
-
     img {
       width: inherit;
     }
   }
+  .bannerContainer {
+    position: absolute;
+    top: 30px;
+    left: 22px;
+
+    .bannerTitle {
+      font-size: 28px;
+      color: #ffffff;
+      line-height: 40px;
+      text-shadow: 0px 2px 4px #17af92;
+    }
+    .bannerSchool {
+      padding-top: 10px;
+      display: flex;
+      align-items: flex-start;
+      max-width: 180px;
+    }
+    .iconSchool {
+      display: inline-block;
+      width: 16px;
+      height: 16px;
+      margin-right: 6px;
+      margin-top: 2px;
+    }
+    .schoolName {
+      font-size: 14px;
+      font-weight: 500;
+      color: #0e8e76;
+      line-height: 20px;
+    }
+  }
   .required {
     color: #ff5a56;
   }
@@ -39,3 +70,43 @@
     }
   }
 }
+
+.popup {
+  width: 276px;
+  border-radius: 8px;
+  overflow: inherit;
+  margin-top: -20px;
+  .popupContainer {
+    position: relative;
+
+    h3 {
+      padding-top: 18px;
+      padding-bottom: 10px;
+      font-size: 17px;
+      font-weight: 500;
+      color: var(--k-gray-1);
+      line-height: 24px;
+      text-align: center;
+    }
+
+    .popupContent {
+      padding: 0 25px 22px;
+      font-size: 14px;
+      color: var(--k-gray-3);
+      line-height: 20px;
+      span {
+        color: var(--k-font-primary);
+      }
+    }
+    .popupBtn {
+      display: block;
+      margin: 0 auto 24px;
+      width: 178px;
+      --van-button-normal-font-size: 16px;
+    }
+  }
+  .bannerPopup {
+    width: 100%;
+    margin-top: -22px;
+  }
+}

+ 38 - 6
src/views/school-register/index.tsx

@@ -1,9 +1,10 @@
 import { defineComponent, reactive } from 'vue';
 import styles from './index.module.less';
-import { Button, Cell, CellGroup, Field, Image } from 'vant';
+import { Button, Cell, CellGroup, Field, Popup } from 'vant';
 import banner from './images/banner.png';
-// import iconSchool from './images/icon-school.png';
+import iconSchool from './images/icon-school.png';
 import iconTips from './images/icon-tips.png';
+import bannerPopup from './images/banner-popup.png';
 import MSticky from '@/components/m-sticky';
 import MProtocol from '@/components/m-protocol';
 
@@ -13,12 +14,25 @@ export default defineComponent({
     const forms = reactive({
       username: '',
       phone: '',
-      isAgree: false
+      isAgree: false,
+      registerStatus: false
     });
+
+    const onSubmit = async () => {
+      forms.registerStatus = true;
+    };
+
     return () => (
       <div class={styles['school-register']}>
         <div class={styles.banner}>
-          <img src={banner} />
+          <img src={banner} alt="banner" />
+          <div class={styles.bannerContainer}>
+            <div class={styles.bannerTitle}>乐团领队注册</div>
+            <div class={styles.bannerSchool}>
+              <img src={iconSchool} class={styles.iconSchool} />
+              <p class={styles.schoolName}>武汉市武昌区中山路小学第一分校</p>
+            </div>
+          </div>
         </div>
 
         <CellGroup inset>
@@ -50,7 +64,7 @@ export default defineComponent({
 
           <Cell>
             <div class={styles.tips}>
-              <Image src={iconTips} class={styles.iconTips} />
+              <img src={iconTips} class={styles.iconTips} />
               提示:手机号码将成为您管乐迷学校端登录账户
             </div>
           </Cell>
@@ -59,11 +73,29 @@ export default defineComponent({
         <MSticky position="bottom">
           <MProtocol style={{ textAlign: 'center' }} />
           <div class={['btnGroupFixed']}>
-            <Button round block type="primary">
+            <Button round block type="primary" onClick={onSubmit}>
               提交
             </Button>
           </div>
         </MSticky>
+
+        <Popup
+          v-model:show={forms.registerStatus}
+          class={styles.popup}
+          closeOnClickOverlay={false}>
+          <div class={styles.popupContainer}>
+            <img src={bannerPopup} class={styles.bannerPopup} />
+            <h3>注册成功</h3>
+            <div class={styles.popupContent}>
+              恭喜您注册成功为武昌区中山路小学<span>【分管领导】</span>
+              ,请下载管乐迷学校端App进行乐团管理吧~
+            </div>
+
+            <Button type="primary" round class={styles.popupBtn}>
+              立即下载
+            </Button>
+          </div>
+        </Popup>
       </div>
     );
   }

+ 69 - 0
src/views/site-management/drop-down-modal.tsx

@@ -0,0 +1,69 @@
+import { Button, Picker, PickerColumn } from 'vant';
+import { PropType, defineComponent, onMounted, reactive, watch } from 'vue';
+
+export default defineComponent({
+  name: 'drop-down-modal',
+  props: {
+    selectValues: {
+      type: [String, Number],
+      default: null
+    },
+    columns: {
+      type: Array as PropType<PickerColumn>,
+      default: () => []
+    },
+    open: {
+      type: Boolean,
+      default: false
+    }
+  },
+  emits: ['dropDownClose', 'dropDownConfirm'],
+  setup(props, { emit }) {
+    const forms = reactive({
+      values: [] as any
+    });
+
+    onMounted(() => {
+      forms.values = [props.selectValues];
+    });
+
+    watch(
+      () => props.selectValues,
+      () => {
+        forms.values = [props.selectValues];
+      }
+    );
+    watch(
+      () => props.open,
+      () => {
+        setTimeout(() => {
+          forms.values = [props.selectValues];
+        }, 100);
+      }
+    );
+    return () => (
+      <>
+        <Picker
+          v-model={forms.values}
+          showToolbar={false}
+          visibleOptionNum={5}
+          columns={props.columns}
+        />
+        <div class={['btnGroupPopup', 'van-hairline--top']}>
+          <Button round onClick={() => emit('dropDownClose')}>
+            取消
+          </Button>
+          <Button
+            type="primary"
+            round
+            onClick={() => {
+              console.log(forms.values);
+              emit('dropDownConfirm', forms.values);
+            }}>
+            确定
+          </Button>
+        </div>
+      </>
+    );
+  }
+});

binární
src/views/site-management/images/icon-empty.png


+ 181 - 0
src/views/site-management/index.module.less

@@ -0,0 +1,181 @@
+.siteManagement {
+  --van-dropdown-menu-title-font-size: 14px;
+  --van-button-normal-font-size: 16px;
+  --van-dropdown-menu-height: 44px;
+
+  :global {
+    .van-dropdown-menu__bar {
+      box-shadow: none;
+    }
+
+    .van-dropdown-item__content {
+      border-radius: 0px 0px 12px 12px;
+    }
+  }
+
+  .iconSetting {
+    font-size: 24px;
+
+    :global {
+      .van-badge--dot {
+        top: 4px;
+        right: 4px;
+        border: 1px solid #fff;
+      }
+    }
+  }
+}
+
+.siteItem {
+  margin: 12px 13px 0;
+  border-radius: 10px;
+  overflow: hidden;
+}
+
+.overhide {
+  max-width: 150px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.cellGroup {
+  :global {
+    .van-cell {
+      padding: 12px;
+    }
+  }
+
+  .orchestraName {
+    display: flex;
+    align-items: center;
+    line-height: 20px;
+    font-size: 14px;
+    color: var(--k-gray-3);
+  }
+
+  .iconMusic {
+    width: 18px;
+    height: 18px;
+    margin-right: 4px;
+  }
+
+  .address {
+    color: var(--k-gray-1);
+  }
+
+  .iconTeacher {
+    width: 48px;
+    height: 48px;
+    border-radius: 50%;
+    margin-right: 8px;
+  }
+
+  .username {
+    padding: 8px 12px 16px;
+    color: var(--k-gray-3);
+    font-size: 14px;
+    line-height: 20px;
+
+    .classname {
+      font-size: 16px;
+      font-weight: 600;
+      color: var(--k-gray-1);
+
+      line-height: 22px;
+    }
+  }
+}
+
+.photoGroup {
+  padding: 12px 12px 20px;
+  display: flex;
+  align-items: center;
+  justify-content: flex-start;
+
+  .photoTitle {
+    position: relative;
+    z-index: 1;
+    display: inline-block;
+    font-size: 14px;
+    color: var(--k-gray-1);
+    font-weight: 400;
+    margin-bottom: 12px;
+
+    &::before {
+      position: absolute;
+      left: 0;
+      bottom: 0;
+      z-index: -1;
+      display: inline-block;
+      content: ' ';
+      width: 100%;
+      height: 8px;
+      background: #FFD1D1;
+    }
+  }
+
+  .photoList {
+    display: flex;
+  }
+
+  .photoDown {
+    margin-left: 24px;
+
+    .photoTitle::before {
+      background: #CBF2FF;
+    }
+  }
+
+  .showPhoto {
+    .photoTitle::before {
+      background: transparent;
+    }
+  }
+
+  .photo {
+    position: relative;
+    width: 46px;
+    height: 46px;
+
+    &+.photo {
+      margin-left: 6px;
+    }
+
+    :global {
+      .van-image {
+        width: inherit;
+        height: inherit;
+      }
+    }
+
+    .photoMore {
+      position: absolute;
+      top: 0;
+      left: 0;
+      bottom: 0;
+      right: 0;
+      background: rgba(0, 0, 0, 0.5);
+      font-size: 14px;
+      color: #fff;
+      // font-weight: bold;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+  }
+
+  .photoEmpty {
+    display: flex;
+    align-items: center;
+    font-size: 12px;
+    color: var(--k-gray-4);
+
+    .iconEmpty {
+      margin-right: 6px;
+      width: 48px;
+      height: 57px;
+    }
+
+  }
+}

+ 253 - 0
src/views/site-management/index.tsx

@@ -0,0 +1,253 @@
+import MHeader from '@/components/m-header';
+import MSticky from '@/components/m-sticky';
+import { Cell, CellGroup, DropdownItem, DropdownMenu, Icon, Image } from 'vant';
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from './index.module.less';
+import DropDownModal from './drop-down-modal';
+import iconSetting from '@/common/images/icon-setting.png';
+import iconMusic from '@/common/images/icon-music.png';
+import iconTeacher from '@/common/images/icon-teacher-default.png';
+// import iconEmpty from './images/icon-empty.png';
+import MImagePreview from '@/components/m-image-preview';
+import SkeletonModal from './skeleton-modal';
+import MEmpty from '@/components/m-empty';
+import MFullRefresh from '@/components/m-full-refresh';
+
+export default defineComponent({
+  name: 'site-management',
+  setup() {
+    const dropDownItemRef = ref();
+    const dropDownItemRef1 = ref();
+    const forms = reactive({
+      loading: true,
+      refreshing: false,
+      titleTimeValue: '',
+      titleOrchestraValue: '',
+      timeColumns: [
+        { text: '全部时间', value: '' },
+        { text: '杭州', value: 'Hangzhou' },
+        { text: '宁波', value: 'Ningbo' },
+        { text: '温州', value: 'Wenzhou' },
+        { text: '绍兴', value: 'Shaoxing' },
+        { text: '湖州', value: 'Huzhou' }
+      ],
+      imageShow: false,
+      startPosition: 0,
+      imagePreview: [
+        'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/d7108568f10844c1b25db8cebf971caa_mergeImage.png',
+        'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/d7d77ad6169c4b688cea7183dfabbae2_mergeImage.png',
+        'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/5714fa27180d4115b5bcae6cc2a9ea4f_mergeImage.png'
+      ] as string[]
+    });
+
+    const onDropDownClose = (item: any) => {
+      item.value && item.value.toggle();
+    };
+
+    const formatName = (type: string) => {
+      if (type === 'time') {
+        let name = '';
+        forms.timeColumns.forEach((item: any) => {
+          if (forms.titleTimeValue === item.value) {
+            name = item.text;
+          }
+        });
+        return name;
+      } else if (type === 'orchestra') {
+        let name = '';
+        forms.timeColumns.forEach((item: any) => {
+          if (forms.titleOrchestraValue === item.value) {
+            name = item.text;
+          }
+        });
+        return name;
+      }
+    };
+
+    onMounted(() => {
+      setTimeout(() => {
+        forms.loading = false;
+      }, 2000);
+    });
+    return () => (
+      <div class={styles.siteManagement}>
+        <MSticky position="top">
+          <MHeader>
+            {{
+              right: () => (
+                <Icon class={styles.iconSetting} name={iconSetting} dot />
+              )
+            }}
+          </MHeader>
+
+          <DropdownMenu>
+            <DropdownItem ref={dropDownItemRef} title={formatName('time')}>
+              <DropDownModal
+                selectValues={forms.titleTimeValue}
+                columns={forms.timeColumns}
+                open={dropDownItemRef.value.state.showPopup}
+                onDropDownClose={() => onDropDownClose(dropDownItemRef)}
+                onDropDownConfirm={(values: any) => {
+                  forms.titleTimeValue = values[0];
+                  console.log(values, 'time');
+                  onDropDownClose(dropDownItemRef);
+                }}
+              />
+            </DropdownItem>
+            <DropdownItem
+              ref={dropDownItemRef1}
+              title={formatName('orchestra')}>
+              <DropDownModal
+                selectValues={forms.titleOrchestraValue}
+                columns={forms.timeColumns}
+                open={dropDownItemRef1.value.state.showPopup}
+                onDropDownClose={() => onDropDownClose(dropDownItemRef1)}
+                onDropDownConfirm={(values: any) => {
+                  forms.titleOrchestraValue = values[0];
+                  console.log(values, 'orchestra');
+                  onDropDownClose(dropDownItemRef1);
+                }}
+              />
+            </DropdownItem>
+          </DropdownMenu>
+        </MSticky>
+
+        <MFullRefresh
+          v-model:modelValue={forms.refreshing}
+          onRefresh={() => {
+            setTimeout(() => {
+              forms.refreshing = false;
+            }, 1000);
+          }}
+          style={{
+            minHeight: `calc(100vh - var(--header-height))`
+          }}>
+          <SkeletonModal v-model:show={forms.loading}>
+            {/* <MEmpty
+            style={{
+              minHeight: `calc(100vh - var(--header-height))`
+            }}
+            description="暂无数据"
+          /> */}
+            <div class={styles.siteItem}>
+              <CellGroup class={styles.cellGroup} border={false}>
+                <Cell border={false} center>
+                  {{
+                    title: () => (
+                      <div class={styles.orchestraName}>
+                        <img src={iconMusic} class={styles.iconMusic} />
+                        <p class={styles.overhide}>
+                          武汉小学乐团武汉小学乐团武汉小学乐团武汉小学乐团
+                        </p>
+                      </div>
+                    ),
+                    default: () => (
+                      <p class={[styles.address, styles.overhide]}>
+                        1年级1班1年级1班1年级1班
+                      </p>
+                    )
+                  }}
+                </Cell>
+                <Cell center class={styles.username}>
+                  {{
+                    icon: () => (
+                      <Image
+                        src={iconTeacher}
+                        class={styles.iconTeacher}
+                        fit="contain"
+                      />
+                    ),
+                    title: () => (
+                      <div>
+                        <div class={styles.classname}>声部课·上低音号</div>
+                        <div class={styles.name}>王丹丹</div>
+                      </div>
+                    )
+                  }}
+                </Cell>
+              </CellGroup>
+              <CellGroup class={styles.cellGroup}>
+                <div class={[styles.photoGroup]}>
+                  <div class={styles.photoUp}>
+                    <h3>
+                      <span class={styles.photoTitle}>课前照片</span>
+                    </h3>
+                    <div class={styles.photoList}>
+                      <div
+                        class={styles.photo}
+                        onClick={() => {
+                          forms.imageShow = true;
+                          forms.startPosition = 0;
+                        }}>
+                        <Image src="https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/d7108568f10844c1b25db8cebf971caa_mergeImage.png" />
+                      </div>
+                      <div
+                        class={styles.photo}
+                        onClick={() => {
+                          forms.imageShow = true;
+                          forms.startPosition = 1;
+                        }}>
+                        <Image src="https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/d7d77ad6169c4b688cea7183dfabbae2_mergeImage.png" />
+                      </div>
+                      <div
+                        class={styles.photo}
+                        onClick={() => {
+                          forms.imageShow = true;
+                          forms.startPosition = 2;
+                        }}>
+                        <Image src="https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/5714fa27180d4115b5bcae6cc2a9ea4f_mergeImage.png" />
+                        <div class={styles.photoMore}>+8</div>
+                      </div>
+                    </div>
+                    {/* <div class={styles.photoEmpty}>
+                  <img src={iconEmpty} class={styles.iconEmpty} />
+                  <p>老师未上传照片~</p>
+                </div> */}
+                  </div>
+                  <div class={styles.photoDown}>
+                    <h3>
+                      <span class={styles.photoTitle}>课后照片</span>
+                    </h3>
+                    <div class={styles.photoList}>
+                      <div
+                        class={styles.photo}
+                        onClick={() => {
+                          forms.imageShow = true;
+                          forms.startPosition = 0;
+                        }}>
+                        <Image src="https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/d7108568f10844c1b25db8cebf971caa_mergeImage.png" />
+                      </div>
+                      <div
+                        class={styles.photo}
+                        onClick={() => {
+                          forms.imageShow = true;
+                          forms.startPosition = 1;
+                        }}>
+                        <Image src="https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/d7d77ad6169c4b688cea7183dfabbae2_mergeImage.png" />
+                      </div>
+                      <div
+                        class={styles.photo}
+                        onClick={() => {
+                          forms.imageShow = true;
+                          forms.startPosition = 2;
+                        }}>
+                        <Image src="https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/5714fa27180d4115b5bcae6cc2a9ea4f_mergeImage.png" />
+                        <div class={styles.photoMore}>+8</div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </CellGroup>
+            </div>
+          </SkeletonModal>
+        </MFullRefresh>
+
+        <MImagePreview
+          v-model:show={forms.imageShow}
+          images={forms.imagePreview}
+          startPosition={forms.startPosition}
+        />
+      </div>
+    );
+  }
+});

+ 127 - 0
src/views/site-management/skeleton-modal.tsx

@@ -0,0 +1,127 @@
+import {
+  Cell,
+  CellGroup,
+  Skeleton,
+  SkeletonAvatar,
+  SkeletonImage,
+  SkeletonParagraph
+} from 'vant';
+import { defineComponent, onMounted, reactive, watch } from 'vue';
+import styles from './index.module.less';
+
+export default defineComponent({
+  name: 'skeleton-modal',
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    showCount: {
+      type: Array,
+      default: () => [1, 2, 3, 4, 5]
+    }
+  },
+  setup(props, { slots }) {
+    const forms = reactive({
+      loading: false
+    });
+
+    onMounted(() => {
+      forms.loading = props.show;
+    });
+
+    watch(
+      () => props.show,
+      () => {
+        forms.loading = props.show;
+      }
+    );
+    return () => (
+      <Skeleton loading={forms.loading} style="flex-wrap: wrap">
+        {{
+          template: () => (
+            <div
+              style={{
+                height: `calc(100vh - var(--header-height))`,
+                overflow: 'hidden'
+              }}>
+              {props.showCount.map(() => (
+                <div class={styles.siteItem}>
+                  <CellGroup class={styles.cellGroup}>
+                    <Cell center>
+                      {{
+                        icon: () => <SkeletonAvatar class={styles.iconMusic} />,
+                        title: () => (
+                          <div
+                            style={{
+                              display: 'flex',
+                              justifyContent: 'space-between'
+                            }}>
+                            <SkeletonParagraph rowWidth={'40%'} />
+                            <SkeletonParagraph
+                              style={{
+                                marginTop: 0
+                              }}
+                              rowWidth={'40%'}
+                            />
+                          </div>
+                        )
+                      }}
+                    </Cell>
+                    <Cell center class={styles.username}>
+                      {{
+                        icon: () => (
+                          <SkeletonAvatar class={styles.iconTeacher} />
+                        ),
+                        title: () => (
+                          <div>
+                            <SkeletonParagraph rowWidth={'40%'} />
+                            <SkeletonParagraph
+                              rowWidth={'40%'}
+                              style={{ marginTop: '4px' }}
+                            />
+                          </div>
+                        )
+                      }}
+                    </Cell>
+                  </CellGroup>
+                  <CellGroup class={styles.cellGroup}>
+                    <div class={[styles.photoGroup]}>
+                      <div class={[styles.photoUp, styles.showPhoto]}>
+                        <h3>
+                          <SkeletonParagraph
+                            class={styles.photoTitle}
+                            rowWidth={'30%'}
+                          />
+                        </h3>
+                        <div class={styles.photoList}>
+                          <SkeletonImage class={styles.photo} />
+                          <SkeletonImage class={styles.photo} />
+                          <SkeletonImage class={styles.photo} />
+                        </div>
+                      </div>
+                      <div class={[styles.photoDown, styles.showPhoto]}>
+                        <h3>
+                          <SkeletonParagraph
+                            class={styles.photoTitle}
+                            rowWidth={'30%'}
+                          />
+                        </h3>
+                        <div class={styles.photoList}>
+                          <SkeletonImage class={styles.photo} />
+                          <SkeletonImage class={styles.photo} />
+                          <SkeletonImage class={styles.photo} />
+                        </div>
+                      </div>
+                    </div>
+                  </CellGroup>
+                </div>
+              ))}
+            </div>
+          ),
+          default: () => slots.default && slots.default()
+        }}
+      </Skeleton>
+    );
+  }
+});

+ 0 - 0
src/views/test.ts