Browse Source

style: 修改样式

chenlei 2 years ago
parent
commit
b2f9b512de

+ 2 - 0
components.d.ts

@@ -13,6 +13,7 @@ declare module 'vue' {
     ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
+    ElDialog: typeof import('element-plus/es')['ElDialog']
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElImage: typeof import('element-plus/es')['ElImage']
@@ -23,6 +24,7 @@ declare module 'vue' {
     SvgIcon: typeof import('./src/components/svg-icon/index.vue')['default']
   }
   export interface ComponentCustomProperties {
+    vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
     vLoading: typeof import('element-plus/es')['ElLoadingDirective']
   }
 }

+ 3 - 5
package.json

@@ -1,13 +1,12 @@
 {
   "name": "js-wlg-app",
-  "version": "0.1.0",
-  "description": "江苏文旅厅 16:9 大屏适配",
+  "version": "1.0.0",
   "private": true,
   "scripts": {
     "serve": "cross-env VUE_APP_BACKEND_URL=https://sit-jswl2.4dage.com vue-cli-service serve",
-    "build": "cross-env VUE_APP_BACKEND_URL=https://sit-jswl2.4dage.com vue-cli-service build",
+    "build": "vue-cli-service build",
     "lint": "vue-cli-service lint",
-    "electron:build": "cross-env VUE_APP_BACKEND_URL=https://sit-jswl2.4dage.com vue-cli-service electron:build",
+    "electron:build": "vue-cli-service electron:build",
     "electron:serve": "cross-env VUE_APP_BACKEND_URL=https://sit-jswl2.4dage.com vue-cli-service electron:serve",
     "postinstall": "electron-builder install-app-deps",
     "postuninstall": "electron-builder install-app-deps"
@@ -20,7 +19,6 @@
     "element-plus": "^2.3.8",
     "lodash": "^4.17.21",
     "vue": "^3.2.13",
-    "vue-awesome-swiper": "^5.0.1",
     "vue-class-component": "^8.0.0-0",
     "vue-router": "^4.0.3",
     "vuex": "^4.0.0"

+ 0 - 41
pnpm-lock.yaml

@@ -23,9 +23,6 @@ dependencies:
   vue:
     specifier: ^3.2.13
     version: 3.3.4
-  vue-awesome-swiper:
-    specifier: ^5.0.1
-    version: registry.npmmirror.com/vue-awesome-swiper@5.0.1(swiper@8.4.7)(vue@3.3.4)
   vue-class-component:
     specifier: ^8.0.0-0
     version: 8.0.0-alpha.1(vue@3.3.4)
@@ -11445,14 +11442,6 @@ packages:
       entities: registry.npmmirror.com/entities@2.2.0
     dev: true
 
-  registry.npmmirror.com/dom7@4.0.6:
-    resolution: {integrity: sha512-emjdpPLhpNubapLFdjNL9tP06Sr+GZkrIHEXLWvOGsytACUrkbeIdjO5g77m00BrHTznnlcNqgmn7pCN192TBA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/dom7/-/dom7-4.0.6.tgz}
-    name: dom7
-    version: 4.0.6
-    dependencies:
-      ssr-window: registry.npmmirror.com/ssr-window@4.0.2
-    dev: false
-
   registry.npmmirror.com/domain-browser@1.2.0:
     resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/domain-browser/-/domain-browser-1.2.0.tgz}
     name: domain-browser
@@ -15038,12 +15027,6 @@ packages:
       tweetnacl: registry.npmmirror.com/tweetnacl@0.14.5
     dev: true
 
-  registry.npmmirror.com/ssr-window@4.0.2:
-    resolution: {integrity: sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/ssr-window/-/ssr-window-4.0.2.tgz}
-    name: ssr-window
-    version: 4.0.2
-    dev: false
-
   registry.npmmirror.com/ssri@6.0.2:
     resolution: {integrity: sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/ssri/-/ssri-6.0.2.tgz}
     name: ssri
@@ -15294,17 +15277,6 @@ packages:
       - supports-color
     dev: true
 
-  registry.npmmirror.com/swiper@8.4.7:
-    resolution: {integrity: sha512-VwO/KU3i9IV2Sf+W2NqyzwWob4yX9Qdedq6vBtS0rFqJ6Fa5iLUJwxQkuD4I38w0WDJwmFl8ojkdcRFPHWD+2g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/swiper/-/swiper-8.4.7.tgz}
-    name: swiper
-    version: 8.4.7
-    engines: {node: '>= 4.7.0'}
-    requiresBuild: true
-    dependencies:
-      dom7: registry.npmmirror.com/dom7@4.0.6
-      ssr-window: registry.npmmirror.com/ssr-window@4.0.2
-    dev: false
-
   registry.npmmirror.com/tapable@1.1.3:
     resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tapable/-/tapable-1.1.3.tgz}
     name: tapable
@@ -15894,19 +15866,6 @@ packages:
     version: 1.1.2
     dev: true
 
-  registry.npmmirror.com/vue-awesome-swiper@5.0.1(swiper@8.4.7)(vue@3.3.4):
-    resolution: {integrity: sha512-mWjFJzUqA4lG+DmsmibvMpoiBnl+IH2SSeiiQ3i5M0t1y9FknTxnGT0DsMb2YdJLgjYMEK3sYOWzqgLnZMH8Lg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vue-awesome-swiper/-/vue-awesome-swiper-5.0.1.tgz}
-    id: registry.npmmirror.com/vue-awesome-swiper/5.0.1
-    name: vue-awesome-swiper
-    version: 5.0.1
-    peerDependencies:
-      swiper: ^7.0.0  || ^8.0.0
-      vue: 3.x
-    dependencies:
-      swiper: registry.npmmirror.com/swiper@8.4.7
-      vue: 3.3.4
-    dev: false
-
   registry.npmmirror.com/vue-demi@0.14.5(vue@3.3.4):
     resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.5.tgz}
     id: registry.npmmirror.com/vue-demi/0.14.5

+ 62 - 24
public/config.js

@@ -1,8 +1,14 @@
 // 场馆 Record<场馆id, string>
 museum = { 3: "苏州博物馆(本馆),苏州博物馆(西馆)" };
 
+// 接口地址
+VUE_APP_BACKEND_URL = "http://114.217.51.225:7077";
+
 // 云游景区
-cloudScenicUrl = "https://www.baidu.com";
+cloudScenicUrl =
+  "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area" +
+  "?T=" +
+  Date.now();
 
 // 单位
 company = "珠海四维看看";
@@ -12,60 +18,92 @@ SCENIC_MUSEUM_POS = [
   {
     id: 37,
     name: "徐州市",
-    left: 229,
-    top: 136,
-    url: "https://baidu.com",
+    top: 133,
+    left: 317,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=320300",
+  },
+  {
+    id: 47,
+    name: "宿迁市",
+    left: 416,
+    top: 251,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=321300",
   },
-  { id: 47, name: "宿迁市", left: 349, top: 227 },
   {
     id: 41,
     name: "连云港市",
-    left: 520,
-    top: 40,
+    left: 570,
+    top: 82,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=320700",
   },
   {
     id: 42,
     name: "淮安市",
-    left: 470,
-    top: 270,
+    left: 531,
+    top: 302,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=320800",
+  },
+  {
+    id: 43,
+    name: "盐城市",
+    top: 304,
+    left: 719,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=320900",
   },
-  { id: 43, name: "盐城市", left: 690, top: 316 },
   {
     id: 44,
     name: "扬州市",
-    left: 540,
-    top: 400,
+    left: 587,
+    top: 392,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=321000",
+  },
+  {
+    id: 46,
+    name: "泰州市",
+    left: 712,
+    top: 605,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=321200",
   },
-  { id: 46, name: "泰州市", left: 700, top: 635 },
   {
     id: 35,
     name: "南京市",
-    left: 374,
-    top: 757,
+    left: 422,
+    top: 690,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=320100",
+  },
+  {
+    id: 45,
+    name: "镇江市",
+    left: 583,
+    top: 675,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=321100",
   },
-  { id: 45, name: "镇江市", left: 555, top: 731 },
   {
     id: 40,
     name: "南通市",
-    left: 892,
-    top: 696,
+    left: 896,
+    top: 647,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=320600",
   },
   {
     id: 38,
     name: "常州市",
-    left: 594,
-    top: 840,
+    left: 633,
+    top: 800,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=320400",
   },
   {
     id: 36,
     name: "无锡市",
-    left: 770,
-    top: 854,
+    left: 767,
+    top: 819,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=320200",
   },
   {
     id: 39,
     name: "苏州市",
-    left: 885,
-    top: 904,
+    left: 888,
+    top: 859,
+    url: "http://127.0.0.1:18080/ms-mechanism-bs/default/on-cloud-scenic-area?areaCode=320500",
   },
 ];

+ 1 - 1
public/index.html

@@ -6,6 +6,7 @@
     <meta name="viewport" content="width=device-width,initial-scale=1.0" />
     <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
     <title><%= htmlWebpackPlugin.options.title %></title>
+    <script src="<%= BASE_URL %>config.js"></script>
   </head>
   <body>
     <noscript>
@@ -18,6 +19,5 @@
     <div id="app"></div>
     <!-- built files will be auto injected -->
     <script src="<%= BASE_URL %>js/flexible.js"></script>
-    <script src="<%= BASE_URL %>config.js"></script>
   </body>
 </html>

+ 4 - 0
src/App.scss

@@ -34,6 +34,10 @@ select {
   font-display: swap;
 }
 
+* {
+  user-select: none;
+}
+
 body {
   font-family: Source Han Sans CN-Regular;
   font-size: 0.175rem /* 14/80 */;

+ 7 - 6
src/App.vue

@@ -3,7 +3,7 @@
     <router-view
       v-slot="{ Component }"
       @click="clearTimer"
-      @mousemove="clearTimer"
+      @touch="clearTimer"
       @touchmove="clearTimer"
     >
       <keep-alive>
@@ -26,19 +26,20 @@ import { onMounted, ref } from "vue";
 import { ScreenSavers } from "@/components";
 import { getScreenConfigApi } from "./api";
 import { GetScreenConfigApiResponse } from "./api/types";
+import { useStore } from "vuex";
 
+const store = useStore();
 const showScreenSavers = ref(false);
 const showScreenTime = ref(60 * 10);
 const autoPlay = ref(false);
 const screenList = ref<GetScreenConfigApiResponse["img"]>([]);
-let time = 0;
 
 onMounted(() => {
   getScreenConfig();
 });
 
 const clearTimer = () => {
-  time = 0;
+  store.dispatch("clearTime");
 };
 const closeScreen = () => {
   clearTimer();
@@ -52,12 +53,12 @@ const getScreenConfig = async () => {
   showScreenTime.value = Number(c.time);
   screenList.value = data.img.map((i) => ({
     ...i,
-    thumb: process.env.VUE_APP_BACKEND_URL + i.thumb,
+    thumb: window.VUE_APP_BACKEND_URL + i.thumb,
   }));
 
   setInterval(() => {
-    time++;
-    if (time >= showScreenTime.value && !showScreenSavers.value) {
+    store.commit("setTime", store.state.time + 1);
+    if (store.state.time >= showScreenTime.value && !showScreenSavers.value) {
       showScreenSavers.value = true;
     }
   }, 1000);

BIN
src/assets/imgs/bg3@2x.jpg


BIN
src/assets/imgs/bg4@2x.jpg


+ 8 - 2
src/background.ts

@@ -1,6 +1,6 @@
 "use strict";
 
-import { app, protocol, BrowserWindow } from "electron";
+import { app, protocol, BrowserWindow, globalShortcut } from "electron";
 import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
 import installExtension, { VUEJS_DEVTOOLS } from "electron-devtools-installer";
 import path from "path";
@@ -23,6 +23,7 @@ async function createWindow() {
       nodeIntegration: process.env
         .ELECTRON_NODE_INTEGRATION as unknown as boolean,
       contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
+      webSecurity: false,
     },
     icon: path.join(__dirname, "./public/imgs/app.png"),
   });
@@ -38,9 +39,14 @@ async function createWindow() {
   }
 
   win.setMenu(null);
-  // win.webContents.openDevTools();
+
+  globalShortcut.register("CommandOrControl+Shift+i", () => {
+    win.webContents.openDevTools();
+  });
 }
 
+app.commandLine.appendSwitch("disable-site-isolation-trials");
+
 // Quit when all windows are closed.
 app.on("window-all-closed", () => {
   // On macOS it is common for applications and their menu bar

+ 5 - 2
src/components/back-btn/index.vue

@@ -9,9 +9,12 @@
 import { useRouter } from "vue-router";
 
 const router = useRouter();
+const props = defineProps<{
+  handleClick?: () => void;
+}>();
 
 const handleBack = () => {
-  router.go(-1);
+  props.handleClick ? props.handleClick() : router.go(-1);
 };
 </script>
 
@@ -19,7 +22,7 @@ const handleBack = () => {
 .back-btn {
   display: flex;
   align-items: center;
-  position: absolute;
+  position: fixed;
   top: 1.0625rem /* 85/80 */;
   left: 1rem /* 80/80 */;
   font-size: 0.3125rem /* 25/80 */;

+ 1 - 0
src/global.d.ts

@@ -9,4 +9,5 @@ interface Window {
     top: number;
     url: string;
   }[];
+  VUE_APP_BACKEND_URL: string;
 }

+ 1 - 1
src/router/index.ts

@@ -27,7 +27,7 @@ const routes: Array<RouteRecordRaw> = [
     component: VenueReservation,
   },
   {
-    path: "/iframe/:url",
+    path: "/iframe/:showBtn/:url",
     name: "iframe",
     component: Iframe,
   },

+ 13 - 3
src/store/index.ts

@@ -1,9 +1,19 @@
 import { createStore } from "vuex";
 
 export default createStore({
-  state: {},
+  state: {
+    time: 0,
+  },
   getters: {},
-  mutations: {},
-  actions: {},
+  mutations: {
+    setTime(state, val: number) {
+      state.time = val;
+    },
+  },
+  actions: {
+    clearTime(ctx) {
+      ctx.commit("setTime", 0);
+    },
+  },
   modules: {},
 });

+ 4 - 1
src/utils/services.ts

@@ -36,7 +36,10 @@ interface Service extends AxiosInstance {
 }
 
 const service: Service = axios.create({
-  baseURL: process.env.VUE_APP_BACKEND_URL,
+  baseURL:
+    process.env.NODE_ENV === "development"
+      ? process.env.VUE_APP_BACKEND_URL
+      : window.VUE_APP_BACKEND_URL,
   timeout: 60000,
   headers: {
     "Cache-Control": "no-cache",

+ 21 - 17
src/views/cloud-museum/components/card.vue

@@ -16,8 +16,10 @@
         }}</span>
       </template>
     </div>
-    <p>展览地点:{{ item.address || "--" }}</p>
-    <p>参展博物馆:{{ item.parentName || "--" }}</p>
+    <p class="limit-line">
+      展览地点:{{ `${item.address}(${item.parentName})` || "--" }}
+    </p>
+    <p class="limit-line">展览时间:{{ item.openTime || "--" }}</p>
   </div>
 </template>
 
@@ -33,7 +35,7 @@ const props = defineProps<{
 const router = useRouter();
 const tags = computed(() => props.item.tag.split(","));
 const thumb = computed(
-  () => `${process.env.VUE_APP_BACKEND_URL}${props.item.thumb}`
+  () => `${window.VUE_APP_BACKEND_URL}${props.item.thumb}`
 );
 
 const handleClick = () => {
@@ -41,6 +43,7 @@ const handleClick = () => {
     name: "iframe",
     params: {
       url: encodeURIComponent(props.item.link),
+      showBtn: 0,
     },
   });
 };
@@ -48,38 +51,39 @@ const handleClick = () => {
 
 <style lang="scss" scoped>
 .cloud-museum-card {
-  padding: 0.225rem /* 18/80 */;
-  width: calc(33.3333% - 0.75rem /* 60/80 */);
-  height: 5.45rem /* 436/80 */;
+  padding: 0.175rem /* 14/80 */;
+  width: calc(25% - 0.375rem /* 30/80 */);
+  height: 4.025rem /* 322/80 */;
   background-color: #faf4ea;
   border-radius: 0.05rem /* 4/80 */;
+  box-sizing: border-box;
   box-shadow: 0px 0px 0.075rem /* 6/80 */ rgba(0, 0, 0, 0.25);
 
   &__img {
-    height: 4.375rem /* 350/80 */;
+    height: 3rem /* 240/80 */;
   }
   &__title {
+    margin: 0.1rem /* 8/80 */ 0 0.0375rem /* 3/80 */;
     display: flex;
     align-items: center;
-    margin: 0.125rem /* 10/80 */ 0 0.05rem /* 4/80 */;
-    font-size: 0.25rem /* 20/80 */;
+    font-size: 0.1875rem /* 15/80 */;
     font-family: Source Han Serif CN-Bold;
     font-weight: bold;
     color: #5c594b;
-    line-height: 0.3375rem /* 27/80 */;
+    line-height: 0.2625rem /* 21/80 */;
   }
   p {
     color: #5c594b;
-    line-height: 0.2625rem /* 21/80 */;
-    font-size: 0.1875rem /* 15/80 */;
+    line-height: 0.1875rem /* 15/80 */;
+    font-size: 0.1375rem /* 11/80 */;
   }
   &__tag {
     flex-shrink: 0;
-    margin-left: 0.125rem /* 10/80 */;
-    padding: 0 0.125rem /* 10/80 */;
-    font-size: 0.15rem /* 12/80 */;
-    height: 0.275rem /* 22/80 */;
-    line-height: 0.275rem /* 22/80 */;
+    margin-left: 0.075rem /* 6/80 */;
+    padding: 0 0.0875rem /* 7/80 */;
+    font-size: 0.1375rem /* 11/80 */;
+    height: 0.25rem /* 20/80 */;
+    line-height: 0.225rem /* 18/80 */;
     color: #8f6c38;
     background: rgba(239, 212, 152, 0.5);
     border-radius: 0.0375rem /* 3/80 */;

+ 16 - 4
src/views/cloud-museum/index.scss

@@ -1,9 +1,17 @@
 .cloud-museum {
-  width: 100vw;
+  width: 100%;
   height: 100vh;
-  overflow: hidden;
-  background: url("../../assets/imgs/bg3@2x.jpg") no-repeat center / cover;
+  overflow-y: auto;
 
+  &__bg {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: -1;
+    background: url("../../assets/imgs/bg3@2x.jpg") no-repeat center / cover;
+  }
   &__search {
     position: absolute;
     top: 1rem /* 80/80 */;
@@ -18,6 +26,7 @@
 
     .el-input {
       --el-input-bg-color: transparent;
+      --el-component-size: 0.4375rem /* 35/80 */;
       font-size: 0.25rem /* 20/80 */;
 
       .el-input__wrapper {
@@ -48,6 +57,9 @@
     text-shadow: 0px 0px 0.1875rem /* 15/80 */ #9f7b46;
   }
   &-scroll {
+    position: relative;
+    z-index: 1;
+
     &__container {
       display: flex;
     }
@@ -155,7 +167,7 @@
   }
   &-scroll {
     padding: 0.075rem /* 6/80 */;
-    margin: 0.425rem /* 34/80 */ 0.7875rem /* 63/80 */ 0;
+    margin: 0.425rem /* 34/80 */ 0.7875rem /* 63/80 */ 0.25rem /* 20/80 */;
     overflow: hidden;
 
     &__container {

+ 91 - 71
src/views/cloud-museum/index.vue

@@ -2,56 +2,58 @@
   <div v-loading="loading" class="cloud-museum">
     <BackBtn />
 
-    <DragView :scrollX="false" :maxTranslateYLength="maxHeight">
-      <div ref="scrollRef">
-        <div class="cloud-museum__title">云上博物</div>
-
-        <div class="cloud-museum__search">
-          <el-input
-            v-model="searchKey"
-            clearable
-            placeholder="请输入关键词"
-            @clear="getCityMuseumList"
-          />
-          <div class="btn" @click="getCityMuseumList" />
-        </div>
+    <div v-infinite-scroll="load" :infinite-scroll-delay="500" class="list">
+      <div class="cloud-museum__title">云上博物</div>
+
+      <div class="cloud-museum__search">
+        <el-input
+          v-model="searchKey"
+          clearable
+          placeholder="请输入关键词"
+          @clear="getCityMuseumList"
+        />
+        <div class="btn" @click="handleSearch" />
+      </div>
 
-        <div class="cloud-museum-city">
-          <div
-            v-for="item in cityList"
-            :key="item.id"
-            :class="[
-              'cloud-museum-city__item',
-              curCity === item.id && 'active',
-            ]"
-            @click="handleCity(item.id)"
-          >
-            {{ item.name }}
-          </div>
+      <div class="cloud-museum-city">
+        <div
+          v-for="item in cityList"
+          :key="item.id"
+          :class="['cloud-museum-city__item', curCity === item.id && 'active']"
+          @click="handleCity(item.id)"
+        >
+          {{ item.name }}
         </div>
+      </div>
 
-        <div class="cloud-museum-type">
-          <div
-            v-for="label in DEFAULT_TYPE"
-            :key="label"
-            :class="['cloud-museum-type__item', curType === label && 'active']"
-            @click="handleType(label)"
-          >
-            {{ label }}
-          </div>
+      <div class="cloud-museum-type">
+        <div
+          v-for="label in DEFAULT_TYPE"
+          :key="label"
+          :class="['cloud-museum-type__item', curType === label && 'active']"
+          @click="handleType(label)"
+        >
+          {{ label }}
         </div>
+      </div>
 
-        <div class="cloud-museum-scroll">
-          <div class="cloud-museum-scroll__container">
-            <Card v-for="item in list" :key="item.id" :item="item" />
-          </div>
+      <div class="cloud-museum-scroll">
+        <div class="cloud-museum-scroll__container">
+          <Card v-for="item in list" :key="item.id" :item="item" />
         </div>
-
-        <!-- <div v-loading="true" class="page-loading" style="height: 40px" /> -->
-
-        <div v-if="!list.length" class="empty">暂无博物馆</div>
       </div>
-    </DragView>
+    </div>
+
+    <div
+      v-if="pLoading"
+      v-loading="true"
+      class="page-loading"
+      style="height: 40px"
+    />
+    <div v-if="noMore && list.length" class="empty">没有更多了</div>
+    <div v-if="!list.length" class="empty">暂无博物馆</div>
+
+    <div class="cloud-museum__bg" />
   </div>
 </template>
 
@@ -62,33 +64,46 @@ import {
   CityMuseumItemType,
   GetCityMuseumListApiRequest,
 } from "@/api/types";
-import { BackBtn, DragView } from "@/components";
-import { nextTick, onActivated, ref } from "vue";
+import { BackBtn } from "@/components";
+import { onActivated, ref } from "vue";
 import Card from "./components/card.vue";
 import { useRoute, useRouter } from "vue-router";
 import "./index.scss";
 
-const scrollRef = ref<null | HTMLElement>(null);
-const maxHeight = ref(0);
 const loading = ref(false);
+const pLoading = ref(false);
 const cityList = ref<CityItem[]>([]);
-const curCity = ref(0);
+const curCity = ref();
 const searchKey = ref("");
 const DEFAULT_TYPE = ["全部类型", "基本陈列", "临时展览", "专题展览"];
 const curType = ref(DEFAULT_TYPE[0]);
 const route = useRoute();
 const router = useRouter();
+
+const noMore = ref(false);
+let total = 0;
+let pageNum = 1;
+let pageSize = 10;
 const list = ref<CityMuseumItemType[]>([]);
+const load = async () => {
+  if (pLoading.value || loading.value || noMore.value || total === 0) return;
+
+  pLoading.value = true;
+  try {
+    pageNum++;
+    await getCityMuseumList(true);
+  } finally {
+    pLoading.value = false;
+  }
+};
 
 onActivated(() => {
   loading.value = true;
+  resetPage();
   curCity.value = Number(route.params.id);
   curType.value = DEFAULT_TYPE[0];
   Promise.all([getCity(), getCityMuseumList()]).finally(() => {
     loading.value = false;
-    nextTick(() => {
-      getScrollArea();
-    });
   });
 });
 
@@ -96,47 +111,52 @@ const getCity = async () => {
   if (cityList.value.length) return;
 
   const { data } = await getCityApi();
-  data.unshift({
-    id: 0,
-    name: "江苏省",
-  });
   cityList.value = data;
 };
 
-const getScrollArea = async () => {
-  if (!scrollRef.value) return;
-  maxHeight.value = scrollRef.value.clientHeight - window.innerHeight + 60;
-};
-
 const handleCity = (id: number) => {
+  resetPage();
   curCity.value = id;
   router.replace({ name: "cloudMuseum", params: { id } });
   getCityMuseumList();
 };
 
 const handleType = (name: string) => {
+  resetPage();
   curType.value = name;
   getCityMuseumList();
 };
 
-const getCityMuseumList = async () => {
+const handleSearch = () => {
+  resetPage();
+  getCityMuseumList();
+};
+
+const resetPage = () => {
+  noMore.value = false;
+  pageNum = 1;
+  total = 0;
+};
+
+const getCityMuseumList = async (load?: boolean) => {
   const params: GetCityMuseumListApiRequest = {
-    pageSize: 0,
-    pageNum: 0,
+    pageSize,
+    pageNum,
     searchKey: searchKey.value,
   };
   if (curType.value !== "全部类型") params.type = curType.value;
   if (curCity.value) params.cityId = curCity.value;
-  loading.value = true;
-  try {
-    const { data } = await getCityMuseumListApi(params);
+
+  const { data } = await getCityMuseumListApi(params);
+  if (load) {
+    list.value = list.value.concat(data.records);
+  } else {
     list.value = data.records;
-  } finally {
-    loading.value = false;
   }
+  total = data.total;
 
-  nextTick(() => {
-    getScrollArea();
-  });
+  if (pageNum * pageSize >= total) {
+    noMore.value = true;
+  }
 };
 </script>

+ 1 - 1
src/views/home/components/card/index.scss

@@ -2,7 +2,7 @@
   position: absolute;
   top: 0;
   left: 0;
-  width: 2.125rem /* 170/80 */;
+  width: 1.75rem /* 140/80 */;
   height: 1.5rem /* 120/80 */;
   z-index: 1;
 

+ 1 - 0
src/views/home/components/card/index.vue

@@ -33,6 +33,7 @@ const handleClick = () => {
         name: "iframe",
         params: {
           url: encodeURIComponent(url.value),
+          showBtn: 0,
         },
       });
   } else {

BIN
src/views/home/imgs/bg2.jpg


BIN
src/views/home/imgs/bg2@2x.jpg


BIN
src/views/home/imgs/bg3.jpg


BIN
src/views/home/imgs/bg3@2x.jpg


+ 1 - 1
src/views/home/index.scss

@@ -3,7 +3,7 @@
   width: 100vw;
   height: 100vh;
   overflow: hidden;
-  background: url("./imgs/bg2.jpg") no-repeat center / 100%;
+  background: url("./imgs/bg2@2x.jpg") no-repeat center / 100%;
 
   // &-primary-card {
   //   position: absolute;

+ 22 - 22
src/views/home/index.vue

@@ -36,60 +36,60 @@ const MUSEUM_POS = [
   {
     id: 37,
     name: "徐州市",
-    left: 229,
-    top: 136,
+    top: 147,
+    left: 307,
   },
-  { id: 47, name: "宿迁市", left: 349, top: 227 },
+  { id: 47, name: "宿迁市", top: 241, left: 393 },
   {
     id: 41,
     name: "连云港市",
-    left: 540,
-    top: 102,
+    top: 110,
+    left: 559,
   },
   {
     id: 42,
     name: "淮安市",
-    left: 515,
-    top: 312,
+    top: 323,
+    left: 546,
   },
-  { id: 43, name: "盐城市", left: 739, top: 316 },
+  { id: 43, name: "盐城市", top: 311, left: 738 },
   {
     id: 44,
     name: "扬州市",
-    left: 574,
-    top: 414,
+    top: 403,
+    left: 586,
   },
-  { id: 46, name: "泰州市", left: 700, top: 635 },
+  { id: 46, name: "泰州市", top: 613, left: 710 },
   {
     id: 35,
     name: "南京市",
-    left: 374,
-    top: 757,
+    top: 698,
+    left: 419,
   },
-  { id: 45, name: "镇江市", left: 555, top: 731 },
+  { id: 45, name: "镇江市", left: 595, top: 703 },
   {
     id: 40,
     name: "南通市",
-    left: 892,
-    top: 696,
+    top: 680,
+    left: 856,
   },
   {
     id: 38,
     name: "常州市",
-    left: 594,
-    top: 840,
+    top: 795,
+    left: 609,
   },
   {
     id: 36,
     name: "无锡市",
-    left: 770,
-    top: 854,
+    top: 812,
+    left: 757,
   },
   {
     id: 39,
     name: "苏州市",
-    left: 885,
-    top: 904,
+    top: 854,
+    left: 868,
   },
 ];
 </script>

+ 1 - 1
src/views/home/scenic.vue

@@ -25,6 +25,6 @@ const MUSEUM_POS = window.SCENIC_MUSEUM_POS;
   width: 100vw;
   height: 100vh;
   overflow: hidden;
-  background: url("./imgs/bg3.jpg") no-repeat center / 100%;
+  background: url("./imgs/bg3@2x.jpg") no-repeat center / 100%;
 }
 </style>

+ 30 - 5
src/views/iframe/index.vue

@@ -1,17 +1,42 @@
 <template>
   <div class="iframe-page">
-    <BackBtn />
-    <iframe v-if="iframeUrl" frameborder="0" :src="iframeUrl" />
+    <BackBtn v-if="showBtn" />
+    <iframe v-if="iframeUrl" ref="iframeRef" frameborder="0" :src="iframeUrl" />
   </div>
 </template>
 
 <script lang="ts" setup>
-import { ref } from "vue";
-import { useRoute } from "vue-router";
+import { computed, onActivated, onDeactivated, ref } from "vue";
+import { useRoute, useRouter } from "vue-router";
 import { BackBtn } from "@/components";
+import { useStore } from "vuex";
 
 const route = useRoute();
-const iframeUrl = ref(decodeURIComponent(route.params.url as string));
+const router = useRouter();
+const iframeRef = ref();
+const store = useStore();
+const showBtn = computed(() => route.params.showBtn === "1");
+const iframeUrl = computed(() =>
+  decodeURIComponent(route.params.url as string)
+);
+
+const goBack = (e: MessageEvent<any>) => {
+  if (typeof e.data === "object" && e.data.command === "back") {
+    router.back();
+  }
+};
+
+onActivated(() => {
+  window.addEventListener("message", goBack);
+  iframeRef.value.onload = () => {
+    iframeRef.value.contentWindow.addEventListener("touchmove", () => {
+      store.dispatch("clearTime");
+    });
+  };
+});
+onDeactivated(() => {
+  window.removeEventListener("message", goBack);
+});
 </script>
 
 <style lang="scss">

+ 2 - 1
src/views/museum-detail/components/card.vue

@@ -29,7 +29,7 @@ const props = defineProps<{
 const router = useRouter();
 const tags = computed(() => props.item.tag.split(","));
 const thumb = computed(
-  () => `${process.env.VUE_APP_BACKEND_URL}${props.item.thumb}`
+  () => `${window.VUE_APP_BACKEND_URL}${props.item.thumb}`
 );
 
 const handleClick = () => {
@@ -37,6 +37,7 @@ const handleClick = () => {
     name: "iframe",
     params: {
       url: encodeURIComponent(props.item.link),
+      showBtn: 1,
     },
   });
 };

+ 69 - 0
src/views/venue-reservation/components/dialog.vue

@@ -0,0 +1,69 @@
+<template>
+  <el-dialog
+    v-model="show"
+    modal-class="ven-popup"
+    :modal="false"
+    :show-close="false"
+  >
+    <slot />
+
+    <div class="ven-popup__close" @click="show = false" />
+  </el-dialog>
+</template>
+
+<script lang="ts" setup>
+import { computed } from "vue";
+
+const props = defineProps<{
+  visible: boolean;
+}>();
+const emits = defineEmits(["update:visible"]);
+
+const show = computed({
+  set(v) {
+    emits("update:visible", v);
+  },
+  get() {
+    return props.visible;
+  },
+});
+</script>
+
+<style lang="scss">
+.ven-popup {
+  --el-bg-color: transparent;
+
+  &__close {
+    position: absolute;
+    top: 0.875rem /* 70/80 */;
+    right: 0.75rem /* 60/80 */;
+    width: 0.5375rem /* 43/80 */;
+    height: 0.4875rem /* 39/80 */;
+    background: url("../imgs/btm_cancel.png") no-repeat center / 100%;
+    z-index: 1;
+  }
+  .el-dialog {
+    position: relative;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-top: 25%;
+    width: 5.8875rem /* 471/80 */;
+    height: 4.2375rem /* 339/80 */;
+    box-shadow: none;
+    transform: translateY(-50%);
+    background: url("../imgs/pop.png") no-repeat center / contain;
+
+    &__header {
+      display: none;
+    }
+    &__body {
+      font-size: 0.375rem /* 30/80 */;
+      text-align: center;
+      color: #fffae9;
+      line-height: 0.5125rem /* 41/80 */;
+      text-shadow: 0px 0px 0.1rem /* 8/80 */ #ffb525;
+    }
+  }
+}
+</style>

BIN
src/views/venue-reservation/imgs/btm_cancel.png


BIN
src/views/venue-reservation/imgs/pop.png


+ 51 - 20
src/views/venue-reservation/index.vue

@@ -41,7 +41,7 @@
         </el-form-item>
         <el-form-item label="所在单位" prop="organ">
           <div class="vr-input">
-            <el-input v-model="form.organ" placeholder="请输入内容" />
+            <el-input v-model="form.organ" readonly placeholder="请输入内容" />
           </div>
         </el-form-item>
         <el-form-item label="参观人员">
@@ -133,6 +133,11 @@
 
       <div class="vr-submit" @click="submit" />
     </div>
+
+    <Dialog v-model:visible="dialogVisible">
+      <p v-if="dialogErrType">请准确填写</p>
+      <p>{{ dialogText }}</p>
+    </Dialog>
   </div>
 </template>
 
@@ -140,9 +145,9 @@
 import { reservationVenueApi } from "@/api";
 import { BackBtn, SvgIcon } from "@/components";
 import { formatDate } from "@/utils";
+import Dialog from "./components/dialog.vue";
 import { FormInstance, FormRules } from "element-plus";
 import { computed, onActivated, onMounted, reactive, ref, watch } from "vue";
-import { useRouter } from "vue-router";
 import NJImg from "./imgs/img_nanjing.jpg";
 import DYHImg from "./imgs/img_yangzhou.jpg";
 import SZImg from "./imgs/img_suzhou.jpg";
@@ -155,13 +160,15 @@ type DateType = {
 };
 
 let visitorId = 0;
-const router = useRouter();
 const curExhibitionId = ref(0);
+const dialogVisible = ref(false);
+const dialogText = ref("");
+const dialogErrType = ref(false);
 const venues = computed(() => {
   const str = window.museum[curExhibitionId.value];
   return str ? str.split(",") : [];
 });
-const DEFAULT_TIME = ["08:00-11:30", "14:00-17:30"];
+const DEFAULT_TIME = ["上午", "下午", "晚上"];
 const DEFAULT_FORM = {
   bookDay: "",
   bootTimeScope: DEFAULT_TIME[0],
@@ -219,7 +226,14 @@ onMounted(() => {
 });
 
 onActivated(() => {
-  // 清除缓存数据
+  clear();
+});
+
+watch(venues, (list) => {
+  form.venues = list[0];
+});
+
+const clear = () => {
   Object.assign(form, {
     ...DEFAULT_FORM,
     bookDay: dateList.value[0].value,
@@ -229,11 +243,7 @@ onActivated(() => {
   visitorList.length = 0;
   visitorList.push({ ...DEFAULT_VISITOR });
   curExhibitionId.value = MUSEUM_LIST[0].id;
-});
-
-watch(venues, (list) => {
-  form.venues = list[0];
-});
+};
 
 const disabledDate = (time: Date) => {
   const today = new Date();
@@ -264,19 +274,38 @@ const initDate = () => {
   dateList.value = stack;
 };
 
+function checkTelephone(telephone: string) {
+  var reg = /^[1][3,4,5,7,8][0-9]{9}$/;
+  return reg.test(telephone);
+}
+
+function checkIdCard(idCard: string) {
+  var reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
+  return reg.test(idCard);
+}
+
 const submit = async () => {
   if (!formRef.value || !(await formRef.value.validate())) return;
 
   for (let i = 0; i < visitorList.length; i++) {
+    let errMsg = "";
     const item = visitorList[i];
-    if (!item.name || !item.phone || !item.idcard) {
-      /* eslint-disable */
-      ElMessage({
-        type: "warning",
-        message: `参观人员第${i + 1}条数据请填写完整`,
-        duration: 4000,
-      });
+
+    if (!item.name) {
+      errMsg = `第${i + 1}个参观人员姓名`;
+    } else if (!checkTelephone(item.phone)) {
+      errMsg = `第${i + 1}个参观人员手机号`;
+    } else if (!checkIdCard(item.idcard)) {
+      errMsg = `第${i + 1}个参观人员身份证号`;
+    }
+
+    if (errMsg) {
+      dialogErrType.value = true;
+      dialogText.value = errMsg;
+      dialogVisible.value = true;
       return;
+    } else {
+      dialogErrType.value = false;
     }
   }
 
@@ -293,11 +322,13 @@ const submit = async () => {
   loading.value = true;
   try {
     await reservationVenueApi(params);
+
+    dialogText.value = "预约成功!";
+    dialogVisible.value = true;
+
+    clear();
   } finally {
     loading.value = false;
-    router.replace({
-      name: "home",
-    });
   }
 };
 

+ 83 - 18
src/views/ver-scroll-home/index.vue

@@ -1,20 +1,22 @@
 <template>
   <div class="scroll-page">
-    <Swiper
-      :loop="false"
-      direction="vertical"
-      :grab-cursor="true"
-      :resistance-ratio="0"
-      style="width: 100vw; height: 100vh; overflow: hidden"
-      @slide-change-transition-end="handleChange"
+    <div
+      class="swiper"
+      @touchstart="onTouchstart"
+      @touchmove="onTouchmove"
+      @touchend="onTouchend"
     >
-      <SwiperSlide>
-        <Home />
-      </SwiperSlide>
-      <SwiperSlide>
+      <div
+        class="swiper-wrapper"
+        :style="{
+          transition: animation ? 'transform linear .2s' : 'none',
+          transform: `translateY(${translateY}px)`,
+        }"
+      >
         <ScenicHome />
-      </SwiperSlide>
-    </Swiper>
+        <Home />
+      </div>
+    </div>
 
     <div class="home-menu">
       <div class="home-menu-list">
@@ -42,7 +44,6 @@
 </template>
 
 <script lang="ts" setup>
-import { Swiper, SwiperSlide } from "vue-awesome-swiper";
 import { useRouter } from "vue-router";
 import Home from "../home/index.vue";
 import ScenicHome from "../home/scenic.vue";
@@ -54,21 +55,29 @@ import SceneryActiveImg from "./imgs/btn_scenery_active@2x.png";
 import ReservationNormalImg from "./imgs/btn_reservation_normal@2x.png";
 import ReservationActiveImg from "./imgs/btn_reservation_active@2x.png";
 
+let startY = 0;
+let _moveY = 0;
+let moveY = 0;
+let scroll = false;
 const router = useRouter();
 const curIndex = ref(0);
+const animation = ref(false);
 const menuList = computed(() => [
   {
     icon: MuseumNormalImg,
     activeIcon: MuseumActiveImg,
     event() {
-      router.push({ name: "cloudMuseum", params: { id: 0 } });
+      router.push({ name: "cloudMuseum", params: { id: 34 } });
     },
   },
   {
     icon: SceneryNormalImg,
     activeIcon: SceneryActiveImg,
     event() {
-      router.push({ name: "iframe", params: { url: window.cloudScenicUrl } });
+      router.push({
+        name: "iframe",
+        params: { url: window.cloudScenicUrl, showBtn: 0 },
+      });
     },
   },
   {
@@ -79,8 +88,64 @@ const menuList = computed(() => [
     },
   },
 ]);
+const translateY = ref(0);
 
-const handleChange = (swiper: any) => {
-  curIndex.value = swiper.snapIndex;
+const onTouchstart = (e: TouchEvent) => {
+  startY = e.changedTouches[0].screenY;
+  scroll = true;
+};
+const onTouchmove = (e: TouchEvent) => {
+  if (!scroll) return;
+
+  moveY = e.changedTouches[0].screenY - startY;
+
+  if (
+    (curIndex.value === 0 && moveY > 0) ||
+    (curIndex.value === 1 && moveY < 0)
+  )
+    return;
+
+  translateY.value = _moveY + moveY;
+};
+const onTouchend = () => {
+  animation.value = true;
+  if (curIndex.value === 0) {
+    if (moveY < -250) {
+      translateY.value = -innerHeight;
+      curIndex.value = 1;
+      _moveY = -innerHeight;
+      return;
+    }
+    translateY.value = 0;
+    curIndex.value = 0;
+    _moveY = 0;
+  } else {
+    if (moveY > 250) {
+      translateY.value = 0;
+      curIndex.value = 0;
+      _moveY = 0;
+      return;
+    }
+    translateY.value = -innerHeight;
+    curIndex.value = 1;
+    _moveY = -innerHeight;
+  }
+  setTimeout(() => {
+    animation.value = false;
+    scroll = false;
+  }, 200);
 };
 </script>
+
+<style lang="scss" scoped>
+.swiper {
+  position: relative;
+  width: 100vw;
+  height: 100vh;
+  overflow: hidden;
+
+  &-wrapper {
+    will-change: transform;
+  }
+}
+</style>

+ 2 - 2
vue.config.js

@@ -18,13 +18,13 @@ module.exports = defineConfig({
     electronBuilder: {
       customFileProtocol: "./",
       builderOptions: {
-        productName: "江苏省文旅厅",
+        productName: "云上博物",
         asar: false,
         nsis: {
           oneClick: false,
           allowToChangeInstallationDirectory: true,
         },
-        win: { target: "nsis", icon: "./public/imgs/app.png" },
+        win: { target: "nsis", icon: "./public/imgs/icon.png" },
       },
     },
   },