소스 검색

我的乐团

skyblued 2 년 전
부모
커밋
8962c1e7c0

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

@@ -143,6 +143,38 @@ export const router: RouteRecordRaw[] = [
       title: '补助确认'
     }
   },
+  {
+    path: '/mine-orchestra',
+    name: 'mine-orchestra',
+    component: () => import('@/views/mine-orchestra/index'),
+    meta: {
+      title: '我的乐团'
+    }
+  },
+  {
+    path: '/photo-list',
+    name: 'photo-list',
+    component: () => import('@/views/mine-orchestra/photo-list/index'),
+    meta: {
+      title: '我的乐团'
+    }
+  },
+  {
+    path: '/photo-list',
+    name: 'photo-list',
+    component: () => import('@/views/mine-orchestra/photo-list/index'),
+    meta: {
+      title: '我的乐团'
+    }
+  },
+  {
+    path: '/photo-list-detail',
+    name: 'photo-list-detail',
+    component: () => import('@/views/mine-orchestra/photo-list/detail'),
+    meta: {
+      title: '我的乐团'
+    }
+  },
 ]
 
 // 不需要登录的路由

BIN
src/views/mine-orchestra/images/bg.png


BIN
src/views/mine-orchestra/images/icon-or.png


BIN
src/views/mine-orchestra/images/icon-photo-default.png


BIN
src/views/mine-orchestra/images/icon_change.png


+ 44 - 0
src/views/mine-orchestra/index.module.less

@@ -0,0 +1,44 @@
+.mineOrchestra {
+  padding-top: 12px;
+}
+.select {
+  height: 45px;
+  .icon {
+    width: 17px;
+    height: 17px;
+    margin-right: 4px;
+    flex-shrink: 0;
+  }
+  :global {
+    .van-cell__title {
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+    .van-cell__right-icon {
+      color: #333;
+      transform: rotate(90deg);
+      margin: 0 0 0 4px;
+    }
+  }
+}
+.tabs {
+  --van-tab-active-text-color: var(--van-primary-color);
+  :global {
+    .van-tab {
+      font-size: 15px;
+      color: #333;
+    }
+    .van-tab--active {
+      color: var(--van-primary-color);
+    }
+    .van-tabs__line {
+      bottom: 20px;
+    }
+  }
+}
+.content {
+  height: calc(100vh - 57px -  var(--van-tabs-line-height) - var(--header-height));
+  overflow: hidden;
+  overflow-y: auto;
+}

+ 118 - 0
src/views/mine-orchestra/index.tsx

@@ -0,0 +1,118 @@
+import OHeader from '@/components/o-header'
+import OSticky from '@/components/o-sticky'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { Cell, CellGroup, Picker, Popup, Tab, Tabs } from 'vant'
+import { defineComponent, onMounted, reactive, ref } from 'vue'
+import styles from './index.module.less'
+import iconOrchestra from './images/icon-or.png'
+import MyClass from './my-class'
+import OEmpty from '@/components/o-empty'
+import OFullRefresh from '@/components/o-full-refresh'
+import OrchestraDeeds from './orchestra-deeds'
+import MyPhoto from './my-photo'
+
+export default defineComponent({
+  name: 'my-orchestra',
+  setup(props, ctx) {
+    const tabActive = ref('乐团相册')
+    const data = reactive({
+      orchestraList: [] as any[],
+      loading: true
+    })
+    const modelData = reactive({
+      orchestra: {} as any,
+      orchestraStatus: false
+    })
+    const getOrchestras = async () => {
+      data.loading = true
+      try {
+        const res: any = await request.post(`${state.platformApi}/orchestra/page`, {
+          data: { page: 1, rows: 1000 }
+        })
+        if (Array.isArray(res?.data?.rows)) {
+          data.orchestraList = res.data.rows
+          modelData.orchestra = res.data.rows[0] || {}
+        }
+      } catch {}
+      data.loading = false
+    }
+    onMounted(() => {
+      getOrchestras()
+    })
+
+    return () => (
+      <div class={styles.mineOrchestra}>
+        <OSticky
+          onGetHeight={(height: number) => {
+            document.documentElement.style.setProperty('--header-height', height + 'px')
+          }}
+        >
+          <OHeader title="我的乐团" />
+        </OSticky>
+        <OFullRefresh
+          v-model:modelValue={data.loading}
+          onRefresh={() => {
+            data.orchestraList = []
+            getOrchestras()
+          }}
+          style="min-height: calc(100vh - var(--van-nav-bar-height) - var(--header-height))"
+        >
+          <CellGroup inset>
+            <Cell
+              class={styles.select}
+              center
+              isLink
+              onClick={() => (modelData.orchestraStatus = true)}
+            >
+              {{
+                icon: () => <img class={styles.icon} src={iconOrchestra} />,
+                title: () => <div class="van-ellipsis">{modelData.orchestra.name}</div>
+              }}
+            </Cell>
+          </CellGroup>
+          {!!data.orchestraList.length && (
+            <Tabs v-model:active={tabActive.value} class={styles.tabs} lazyRender={true} background="transparent" animated swipeable>
+              <Tab name="我的班级" title="我的班级">
+                <div class={styles.content}>
+                  <MyClass orchestraId={modelData.orchestra?.id || ''} />
+                </div>
+              </Tab>
+              <Tab name="乐团相册" title="乐团相册">
+                <div class={styles.content}>
+                    <MyPhoto orchestraId={modelData.orchestra?.id || ''} />
+                </div>
+              </Tab>
+              <Tab name="乐团事迹" title="乐团事迹">
+                <div class={styles.content}>
+                    <OrchestraDeeds orchestraId={modelData.orchestra?.id || ''} />
+                </div>
+              </Tab>
+            </Tabs>
+          )}
+          {!data.loading && !data.orchestraList.length && (
+            <OEmpty btnStatus={false} tips="暂无乐团" />
+          )}
+        </OFullRefresh>
+
+        <Popup v-model:show={modelData.orchestraStatus} position="bottom" round>
+          <Picker
+            columns={data.orchestraList}
+            columnsFieldNames={{ text: 'name', value: 'id' }}
+            onCancel={() => (modelData.orchestraStatus = false)}
+            onConfirm={({ selectedValues }: any) => {
+              const val = selectedValues[0] || ''
+              modelData.orchestraStatus = false
+              if (val == modelData.orchestra?.id) {
+                return
+              }
+              const active = data.orchestraList.find((n: any) => n.id == val) || {}
+              modelData.orchestra = active
+              console.log(active)
+            }}
+          />
+        </Popup>
+      </div>
+    )
+  }
+})

+ 60 - 0
src/views/mine-orchestra/my-class/index.module.less

@@ -0,0 +1,60 @@
+.myClass {
+  padding: 12px 0;
+}
+.itemDiv {
+  border-radius: 10px;
+  overflow: hidden;
+  background-color: #fff;
+  margin: 0 12px 12px 12px;
+  .iconImg {
+    width: 44px;
+    height: 44px;
+    border-radius: 50%;
+    overflow: hidden;
+    margin-right: 8px;
+  }
+  .messageImg {
+    width: 24px;
+    height: 24px;
+  }
+  .tag {
+    padding: 0 8px;
+    height: 19px;
+    line-height: 19px;
+    background: #ff8057;
+    border-radius: 4px;
+    color: #fff;
+    font-size: 12px;
+    margin-left: 6px;
+  }
+  :global {
+    .van-cell {
+      padding: 0 12px;
+      height: 68px;
+      .van-tag {
+        margin-left: 4px;
+      }
+      .van-cell__label {
+        color: #777;
+      }
+      .van-cell__value {
+        flex: none;
+      }
+    }
+  }
+  .content {
+    display: flex;
+    align-items: center;
+  }
+}
+.grid {
+  .title {
+    font-size: 24px;
+    color: #333;
+    margin-bottom: 4px;
+  }
+  .name {
+    font-size: 12px;
+    color: #777;
+  }
+}

+ 135 - 0
src/views/mine-orchestra/my-class/index.tsx

@@ -0,0 +1,135 @@
+import { Cell, CellGroup, Grid, GridItem, Image, List, Tag } from 'vant'
+import { defineComponent, onMounted, reactive } from 'vue'
+import styles from './index.module.less'
+import iconTeacher from '@common/images/icon_teacher.png'
+import iconMessage from '@common/images/icon-muit-message.png'
+import { postMessage } from '@/helpers/native-message'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { openDefaultWebView } from '../../../state'
+
+export default defineComponent({
+  name: 'my-orchestra',
+  props: {
+    orchestraId: {
+      type: String,
+      default: ''
+    }
+  },
+  setup(props) {
+    console.log('🚀 ~ props', props)
+    const onMessage = async (item: any) => {
+      console.log(item)
+      postMessage({
+        api: 'joinChatGroup',
+        content: {
+          type: 'multi', // single 单人 multi 多人
+          id: item.imGroupId
+        }
+      })
+    }
+    const openClassDetail = (item: any) => {
+      openDefaultWebView(`/classDetail?classId=${item.id}`)
+    }
+    const data = reactive({
+      pages: {
+        page: 1,
+        rows: 20
+      },
+      classList: [] as any[],
+      loading: false,
+      finished: false
+    })
+    const modelData = reactive({
+      orchestra: {} as any,
+      orchestraStatus: false
+    })
+    const getClassList = async () => {
+      data.loading = true
+      try {
+        const res: any = await request.post(`${state.platformApi}/classGroup/page`, {
+          data: {
+            ...data.pages,
+            orchestraId: props.orchestraId
+          }
+        })
+        console.log(data)
+        data.pages.page += 1
+        if (Array.isArray(res?.data?.rows)) {
+          const list = res.data.rows.map((n: any) => {
+            return {
+              ...n,
+              courseNum: Number(n.courseScheduleNum) - Number(n.completeCourseScheduleNum)
+            }
+          })
+          data.classList = ([] as any[]).concat(data.classList, list)
+          data.finished = res.data.rows.length == 0 ? true : false
+        }
+      } catch {}
+      data.loading = false
+    }
+    onMounted(() => {
+      getClassList()
+    })
+    return () => (
+      <div class={styles.myClass}>
+        <List
+          v-model:loading={data.loading}
+          finishedText="没有更多数据"
+          finished={data.finished}
+          onLoad={getClassList}
+          immediateCheck={false}
+        >
+          {Array.isArray(data.classList) &&
+            data.classList.map((item: any) => (
+              <div class={styles.itemDiv} onClick={() => openClassDetail(item)}>
+                <Cell center label={item.orchestraName}>
+                  {{
+                    icon: () => (
+                      <Image
+                        src={item.teacherAvatar || iconTeacher}
+                        class={styles.iconImg}
+                        fit="cover"
+                      />
+                    ),
+                    title: () => (
+                      <div class={styles.content}>
+                        <div class={['van-ellipsis', styles.teacherName]}>{item.teacherName}</div>
+                        <div class={styles.tag}>
+                          {item.name}
+                        </div>
+                      </div>
+                    ),
+                    value: () => (
+                      <Image
+                        class={styles.messageImg}
+                        src={iconMessage}
+                        onClick={(e: Event) => {
+                          e.stopPropagation()
+                          onMessage(item)
+                        }}
+                      />
+                    )
+                  }}
+                </Cell>
+                <Grid border={false} columnNum={3} class={styles.grid}>
+                  <GridItem>
+                    <p class={styles.title}>{item.preStudentNum || 0}</p>
+                    <p class={styles.name}>学生人数</p>
+                  </GridItem>
+                  <GridItem>
+                    <p class={[styles.title]}>{item.courseNum || 0}</p>
+                    <p class={styles.name}>剩余课时</p>
+                  </GridItem>
+                  <GridItem>
+                    <p class={styles.title}>{item.courseScheduleNum || 0}</p>
+                    <p class={styles.name}>总课时</p>
+                  </GridItem>
+                </Grid>
+              </div>
+            ))}
+        </List>
+      </div>
+    )
+  }
+})

+ 35 - 0
src/views/mine-orchestra/my-photo/index.module.less

@@ -0,0 +1,35 @@
+.photoWrap{
+  display: flex;
+  flex-wrap: wrap;
+  padding: 12px;
+  box-sizing: border-box;
+  justify-content: space-between;
+  div{
+    box-sizing: border-box;
+  }
+}
+.photoItem{
+  position: relative;
+  width: 49%;
+  margin-bottom: 12px;
+  .gridImg{
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    height: 170px;
+    width: 100%;
+  }
+  .iconImage{
+    display: flex;
+    justify-content: center;
+  }
+  .gridName{
+    font-size: 16px;
+    color:#333;
+    padding: 6px 0 4px 0;
+  }
+  .gridDes {
+    color: #777;
+    font-size: 12px;
+  }
+}

+ 108 - 0
src/views/mine-orchestra/my-photo/index.tsx

@@ -0,0 +1,108 @@
+import OEmpty from '@/components/o-empty'
+import OHeader from '@/components/o-header'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { Grid, GridItem, Image, List, showImagePreview } from 'vant'
+import { defineComponent, onMounted, reactive } from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+import styles from './index.module.less'
+import iconImage from '../images/icon-photo-default.png'
+export default defineComponent({
+  name: 'photo-detail',
+  props: {
+    orchestraId: {
+      type: String,
+      default: ''
+    }
+  },
+  setup(props) {
+    const route = useRoute()
+    const router = useRouter()
+    const data = reactive({
+      loading: false,
+      finished: false,
+      pages: {
+        page: 1,
+        rows: 20
+      },
+      list: [] as any[]
+    })
+
+    const getList = async () => {
+      data.loading = true
+      try {
+        const res = await request.post(`${state.platformApi}/orchestraPhotoAlbum/page`, {
+          data: {
+            ...data.pages,
+            orchestraId: props.orchestraId
+          }
+        })
+        if (Array.isArray(res?.data?.rows)) {
+          data.list = data.list.concat(res.data.rows)
+          data.pages.page += 1
+          if (!res.data.rows.length) {
+            data.finished = true
+          }
+        }
+      } catch {}
+      data.loading = false
+    }
+
+    // 预览图片
+    const onShowImage = (index: number) => {
+      const files = data.list.map((file: any) => {
+        return file.fileUrl
+      })
+
+      showImagePreview({
+        images: files,
+        startPosition: index,
+        closeable: true
+      })
+    }
+
+    onMounted(() => {
+      getList()
+      document.title = (route.query.name as any) || ''
+    })
+
+    return () => (
+      <div class={styles.phoneDetail}>
+        {!data.loading && !!data.list.length && (
+          <List
+            v-model:loading={data.loading}
+            finished={data.finished}
+            finishedText="没有更多数据"
+            onLoad={getList}
+            immediateCheck={false}
+          >
+            <div class={styles.photoWrap}>
+              {data.list.map((item: any, index: number) => (
+                <div class={styles.photoItem} onClick={() => {
+                  router.push({
+                    path: '/photo-list',
+                    query: {
+                      id: item.id
+                    }
+                  })
+                }}>
+                  {item.coverUrl ? (
+                    <Image class={styles.gridImg} src={item.coverUrl} fit="cover" />
+                  ) : (
+                    <div class={styles.gridImg}>
+                      <Image src={iconImage} fit="cover" />
+                    </div>
+                  )}
+
+                  <div class={styles.gridName}>{item.name}</div>
+                  <div class={styles.gridDes}>{item.photoCount}张</div>
+                </div>
+              ))}
+            </div>
+          </List>
+        )}
+        {!data.loading && !data.list.length && <OEmpty btnStatus={false} tips="暂无相册" />}
+      </div>
+    )
+  }
+})

BIN
src/views/mine-orchestra/orchestra-deeds/images/icon-edit.png


BIN
src/views/mine-orchestra/orchestra-deeds/images/icon-step-calendar.png


BIN
src/views/mine-orchestra/orchestra-deeds/images/icon-step.png


BIN
src/views/mine-orchestra/orchestra-deeds/images/icon-upload-video-cover.png


BIN
src/views/mine-orchestra/orchestra-deeds/images/icon-upload-video.png


BIN
src/views/mine-orchestra/orchestra-deeds/images/icon-upload.png


+ 81 - 0
src/views/mine-orchestra/orchestra-deeds/index.module.less

@@ -0,0 +1,81 @@
+.cellGroup {
+  margin: 12px 13px 0;
+  overflow: hidden;
+  border-radius: 10px;
+  :global {
+    .van-cell {
+      font-size: 16px;
+      color: #333333;
+      padding: 16px 12px;
+    }
+  }
+}
+
+.storySteps {
+  background-color: transparent;
+  padding-top: 12px;
+
+  :global {
+    .van-step--vertical {
+      padding-right: 13px;
+      &::after {
+        border-width: 0 !important;
+      }
+    }
+  }
+
+  .stepTimes {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-size: 20px;
+    font-weight: bold;
+    color: #333333;
+
+    .stepTime {
+      font-family: 'DINA';
+    }
+
+    .stepEdit {
+      font-size: 14px;
+      font-weight: 400;
+      color: #777777;
+      display: flex;
+      align-items: center;
+      :global {
+        .van-icon {
+          font-size: 14px;
+          margin-right: 3px;
+        }
+      }
+    }
+  }
+
+  .content {
+    padding-top: 8px;
+    font-size: 14px;
+    color: #666666;
+    line-height: 20px;
+  }
+
+  .storySwipe {
+    padding-top: 6px;
+    --van-swipe-indicator-size: 8px;
+
+    .swipeImg {
+      width: 100%;
+      height: 200px;
+      border-radius: 10px;
+      overflow: hidden;
+    }
+  }
+
+  .iconActive {
+    width: 18px;
+    height: 18px;
+  }
+  .iconInactive {
+    width: 12px;
+    height: 12px;
+  }
+}

+ 138 - 0
src/views/mine-orchestra/orchestra-deeds/index.tsx

@@ -0,0 +1,138 @@
+import OHeader from '@/components/o-header'
+import {
+  Cell,
+  CellGroup,
+  Icon,
+  Image,
+  List,
+  Picker,
+  Popup,
+  Step,
+  Steps,
+  Swipe,
+  SwipeItem
+} from 'vant'
+import { defineComponent, onMounted, reactive } from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+import styles from './index.module.less'
+import iconStep from './images/icon-step.png'
+import iconStepCalendar from './images/icon-step-calendar.png'
+import request from '@/helpers/request'
+import OEmpty from '@/components/o-empty'
+import dayjs from 'dayjs'
+import OVideo from '@/components/o-video'
+import {state as globalState} from '@/state'
+
+export default defineComponent({
+  name: 'orchestra-story',
+  props: {
+    orchestraId: {
+      type: String,
+      default: ''
+    }
+  },
+  setup(props) {
+    const state = reactive({
+      isClick: false,
+      list: [] as any,
+      listState: {
+        dataShow: true, // 判断是否有数据
+        loading: false,
+        finished: false,
+        refreshing: false,
+        height: 0 // 页面头部高度,为了处理下拉刷新用的
+      },
+      params: {
+        type: null,
+        page: 1,
+        rows: 20
+      }
+    })
+
+    const getList = async () => {
+      try {
+        if (state.isClick) return
+        state.isClick = true
+        const res = await request.post(`${globalState.platformApi}/orchestraStory/page`, {
+          data: {
+            orchestraId: props.orchestraId
+          }
+        })
+        state.listState.loading = false
+        state.listState.refreshing = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (state.list.length > 0 && result.current === 1) {
+          return
+        }
+        state.list = state.list.concat(result.rows || [])
+        state.listState.finished = result.current >= result.pages
+        state.params.page = result.current + 1
+        state.listState.dataShow = state.list.length > 0
+        state.isClick = false
+      } catch {
+        state.listState.dataShow = false
+        state.listState.finished = true
+        state.listState.refreshing = false
+        state.isClick = false
+      }
+    }
+
+    onMounted(() => {
+      getList()
+    })
+    return () => (
+      <div class={styles.orchestraStory}>
+        {state.listState.dataShow ? (
+          <List
+            v-model:loading={state.listState.loading}
+            finished={state.listState.finished}
+            finishedText=" "
+            class={[styles.liveList]}
+            onLoad={getList}
+            immediateCheck={false}
+          >
+            <Steps direction="vertical" class={styles.storySteps}>
+              {state.list.map((item: any) => (
+                <Step
+                  v-slots={{
+                    'inactive-icon': () => <Image src={iconStep} class={styles.iconInactive} />,
+                    'active-icon': () => <Image src={iconStepCalendar} class={styles.iconActive} />
+                  }}
+                >
+                  <div class={styles.stepTimes}>
+                    <div class={styles.stepTime}>
+                      {dayjs(item.createTime).format('YYYY年MM月DD日')}
+                    </div>
+                  </div>
+                  <p class={[styles.content, 'van-multi-ellipsis--l2']}>{item.content}</p>
+
+                  <Swipe class={styles.storySwipe}>
+                    {item.attachments &&
+                      item.attachments.map((child: any) => (
+                        <SwipeItem>
+                          {item.type === 'IMAGE' && (
+                            <Image src={child.url} class={styles.swipeImg} fit="cover" />
+                          )}
+                          {item.type === 'VIDEO' && (
+                            <OVideo
+                              src={child.url}
+                              height={'100%'}
+                              poster={child.coverImage}
+                              class={styles.swipeImg}
+                            />
+                          )}
+                        </SwipeItem>
+                      ))}
+                  </Swipe>
+                </Step>
+              ))}
+            </Steps>
+          </List>
+        ) : (
+          <OEmpty btnStatus={false} tips="暂无事迹" />
+        )}
+      </div>
+    )
+  }
+})

+ 105 - 0
src/views/mine-orchestra/photo-list/detail.tsx

@@ -0,0 +1,105 @@
+import OEmpty from '@/components/o-empty'
+import OHeader from '@/components/o-header'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { Grid, GridItem, Image, List, showImagePreview } from 'vant'
+import { defineComponent, onMounted, reactive } from 'vue'
+import { useRoute } from 'vue-router'
+import styles from './index.module.less'
+import iconImage from '../images/icon-photo-default.png'
+import OSticky from '@/components/o-sticky'
+export default defineComponent({
+  name: 'photo-detail',
+  props: {
+    orchestraId: {
+      type: String,
+      default: ''
+    }
+  },
+  setup(props) {
+    const route = useRoute()
+    console.log("🚀 ~ route", route)
+    const data = reactive({
+      loading: false,
+      finished: false,
+      pages: {
+        page: 1,
+        rows: 20
+      },
+      list: [] as any[]
+    })
+
+    const getList = async () => {
+      data.loading = true
+      try {
+        const res = await request.get(`${state.platformApi}/orchestraPhoto/page`, {
+          data: {
+            ...data.pages,
+            orchestraPhotoAlbumId: route.query.orchestraPhotoAlbumId
+          }
+        })
+        if (Array.isArray(res?.data?.rows)) {
+          data.list = data.list.concat(res.data.rows)
+          data.pages.page += 1
+          if (!res.data.rows.length) {
+            data.finished = true
+          }
+        }
+      } catch {}
+      data.loading = false
+    }
+
+    // 预览图片
+    const onShowImage = (index: number) => {
+      const files = data.list.map((file: any) => {
+        return file.fileUrl
+      })
+
+      showImagePreview({
+        images: files,
+        startPosition: index,
+        closeable: true
+      })
+    }
+
+    onMounted(() => {
+      getList()
+      document.title = (route.query.name as any) || ''
+    })
+
+    return () => (
+      <div>
+        <OSticky>
+            <OHeader title={(route.query.name as string) || '我的乐团'} />
+        </OSticky>
+        <div class={styles.phoneDetail}>
+          {!data.loading && !!data.list.length && (
+            <List
+              v-model:loading={data.loading}
+              finished={data.finished}
+              finishedText="没有更多数据"
+              onLoad={getList}
+              immediateCheck={false}
+            >
+              <Grid columnNum={3}>
+                {data.list.map((item: any, index: number) => (
+                  <GridItem onClick={() => onShowImage(index)}>
+                    {item.coverUrl ? (
+                      <Image class={styles.gridImg} src={item.coverUrl} fit="cover" />
+                    ) : (
+                      <div class={styles.gridImg}>
+                        <Image src={iconImage} fit="cover" />
+                      </div>
+                    )}
+
+                  </GridItem>
+                ))}
+              </Grid>
+            </List>
+          )}
+          {!data.loading && !data.list.length && <OEmpty btnStatus={false} tips="暂无相册" />}
+        </div>
+      </div>
+    )
+  }
+})

+ 36 - 0
src/views/mine-orchestra/photo-list/index.module.less

@@ -0,0 +1,36 @@
+.photoWrap{
+    display: flex;
+    flex-wrap: wrap;
+    padding: 12px;
+    box-sizing: border-box;
+    justify-content: space-between;
+    div{
+      box-sizing: border-box;
+    }
+  }
+  .photoItem{
+    position: relative;
+    width: 49%;
+    margin-bottom: 12px;
+    .gridImg{
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      height: 170px;
+      width: 100%;
+    }
+    .iconImage{
+      display: flex;
+      justify-content: center;
+    }
+    .gridName{
+      font-size: 16px;
+      color:#333;
+      padding: 6px 0 4px 0;
+    }
+    .gridDes {
+      color: #777;
+      font-size: 12px;
+    }
+  }
+  

+ 108 - 0
src/views/mine-orchestra/photo-list/index.tsx

@@ -0,0 +1,108 @@
+import OEmpty from '@/components/o-empty'
+import OHeader from '@/components/o-header'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { Grid, GridItem, Image, List, showImagePreview } from 'vant'
+import { defineComponent, onMounted, reactive } from 'vue'
+import { useRoute } from 'vue-router'
+import styles from './index.module.less'
+import iconImage from '../images/icon-photo-default.png'
+import OSticky from '@/components/o-sticky'
+export default defineComponent({
+  name: 'photo-detail',
+  props: {
+    orchestraId: {
+      type: String,
+      default: ''
+    }
+  },
+  setup(props) {
+    const route = useRoute()
+    console.log("🚀 ~ route", route)
+    const data = reactive({
+      loading: false,
+      finished: false,
+      pages: {
+        page: 1,
+        rows: 20
+      },
+      list: [] as any[]
+    })
+
+    const getList = async () => {
+      data.loading = true
+      try {
+        const res = await request.post(`${state.platformApi}/orchestraPhotoAlbum/page`, {
+          data: {
+            ...data.pages,
+            orchestraId: props.orchestraId,
+            parentId: route.query.id
+          }
+        })
+        if (Array.isArray(res?.data?.rows)) {
+          data.list = data.list.concat(res.data.rows)
+          data.pages.page += 1
+          if (!res.data.rows.length) {
+            data.finished = true
+          }
+        }
+      } catch {}
+      data.loading = false
+    }
+
+    // 预览图片
+    const onShowImage = (index: number) => {
+      const files = data.list.map((file: any) => {
+        return file.fileUrl
+      })
+
+      showImagePreview({
+        images: files,
+        startPosition: index,
+        closeable: true
+      })
+    }
+
+    onMounted(() => {
+      getList()
+      document.title = (route.query.name as any) || ''
+    })
+
+    return () => (
+      <div>
+        <OSticky>
+            <OHeader title={(route.query.name as string) || '我的乐团'} />
+        </OSticky>
+        <div class={styles.phoneDetail}>
+          {!data.loading && !!data.list.length && (
+            <List
+              v-model:loading={data.loading}
+              finished={data.finished}
+              finishedText="没有更多数据"
+              onLoad={getList}
+              immediateCheck={false}
+            >
+              <div class={styles.photoWrap}>
+                {data.list.map((item: any, index: number) => (
+                  <div class={styles.photoItem}>
+                    {item.coverUrl ? (
+                      <Image class={styles.gridImg} src={item.coverUrl} fit="cover" />
+                    ) : (
+                      <div class={styles.gridImg}>
+                        <Image src={iconImage} fit="cover" />
+                      </div>
+                    )}
+
+                    <div class={styles.gridName}>{item.name || ''}</div>
+                    <div class={styles.gridDes}>{item.photoCount || 0}张</div>
+                  </div>
+                ))}
+              </div>
+            </List>
+          )}
+          {!data.loading && !data.list.length && <OEmpty btnStatus={false} tips="暂无相册" />}
+        </div>
+      </div>
+    )
+  }
+})