gemercheung 9 månader sedan
förälder
incheckning
f10dffc8aa

+ 2 - 1
.eslintrc

@@ -16,6 +16,7 @@
     "sourceType": "module"
   },
   "rules": {
-    "@typescript-eslint/ban-ts-comment": "off"
+    "@typescript-eslint/ban-ts-comment": "off",
+    "@typescript-eslint/no-explicit-any": "off"
   }
 }

+ 17 - 3
src/App.vue

@@ -15,7 +15,7 @@
             <n-icon size="24" :component="ChevronBackOutline" />
             <span style="position: relative; top: -6px">返回我的场景</span>
           </div>
-          <n-button type="primary"> 保存 </n-button>
+          <n-button type="primary" @click="handleEditorSave"> 保存</n-button>
         </div>
       </n-layout-header>
       <n-layout
@@ -58,7 +58,7 @@
               ref="sceneRef"
               :style="{
                 width: sceneRefWidth,
-                visibility: showScene ? 'hidden' : 'visible',
+                visibility: showScene ? 'hidden' : 'visible'
                 // display: showScene ? 'none' : 'block'
               }"
             >
@@ -104,6 +104,8 @@ import { computed, ref, toRaw, watchEffect, h, onMounted, watch } from 'vue'
 import type { Component } from 'vue'
 import { useMainStore } from '@/store'
 import themeOverrides from '@/styles/theme.js'
+import { editorSave } from '@/api/module/editor'
+
 import { useRouter, useRoute } from 'vue-router'
 import {
   ChevronBackOutline,
@@ -126,6 +128,7 @@ const route = useRoute()
 const activeKey = ref(route.name)
 
 const showScene = ref(false)
+// const message = useMessage()
 
 watch(
   () => route.name,
@@ -149,7 +152,11 @@ const theme = computed(() => (mode.value ? darkTheme : lightTheme))
 
 const sceneRefWidth = computed(() => {
   console.log('route', route, route.name)
-  if (route.name === 'basicSettings' || route.name === 'topicNavigation' || route.name === 'digitalHuman')
+  if (
+    route.name === 'basicSettings' ||
+    route.name === 'topicNavigation' ||
+    route.name === 'digitalHuman'
+  )
     return `calc(100% - ${240}px)`
   if (route.name === 'message') return `calc(100% - ${0}px)`
   return `calc(100% - ${mode.sceneRefWidth}px)`
@@ -265,9 +272,16 @@ const handleUpdateValue = (value: string) => {
 onMounted(() => {
   const sceneCode = params.m ? String(params.m) : 'KJ-t-3Y6dxgyehDk'
   main.setSceneCode(sceneCode)
+  main.getSceneInfo()
   setSceneLink(`/page/spg.html?m=${main.sceneCode}`)
   setSceneRef(sceneRef.value)
 })
+
+const handleEditorSave = async () => {
+  const data = main.getEditorData
+  await editorSave(data)
+  // message.success('保存成功!')
+}
 </script>
 
 <style lang="sass">

+ 21 - 0
src/api/module/editor.ts

@@ -0,0 +1,21 @@
+import { Alova, url } from '../alova'
+
+export interface EditorSaveParams {
+  baseSetting: any
+  aiSetting: any
+  navigation: any
+  num: any
+}
+
+export const getInfo = (num: string) => {
+  return Alova.Get(url.getInfo, {
+    params: {
+      num
+    }
+  })
+}
+export const editorSave = (params: EditorSaveParams) => {
+  return Alova.Post(url.editorSave, {
+    ...params
+  })
+}

+ 2 - 1
src/api/url.ts

@@ -8,7 +8,8 @@ const url = {
   messageAdd: '/service/scene/scrb/leaveWord/add',
   messageDel: '/service/scene/edit/scrb/leaveWord/delete',
   messageAudit: '/service/scene/edit/scrb/leaveWord/audit',
-
+  editorSave: '/service/scene/edit/scrb/save',
+  getInfo: '/service/scene/getInfo'
 }
 
 export { url }

+ 21 - 12
src/router/index.ts

@@ -1,50 +1,59 @@
-import { createRouter, createWebHistory,createWebHashHistory } from 'vue-router'
+import {
+  createRouter,
+  createWebHistory,
+  createWebHashHistory
+} from 'vue-router'
 
 const router = createRouter({
   history: createWebHashHistory(),
   routes: [
     {
       path: '/',
-      redirect: '/basicSettings',
+      redirect: '/basicSettings'
       // name: 'home',
       // component: () => import('@/views/HelloWorld.vue'),
       // props: {
       //   msg: 'Hello Vue 3 + TypeScript + Vite + Tailwind CSS/UI + Pinia + Vue Router'
       // }
-    },{
+    },
+    {
       path: '/basicSettings',
       name: 'basicSettings',
       component: () => import('@/views/basicSettings/index.vue'),
       props: {
-        msg: 'Hello Vue 3 + TypeScript + Vite + Tailwind CSS/UI + Pinia + Vue Router'
+        msg: ''
       }
-    },{
+    },
+    {
       path: '/digitalHuman',
       name: 'digitalHuman',
       component: () => import('@/views/digitalHuman/index.vue'),
       props: {
-        msg: 'Hello Vue 3 + TypeScript + Vite + Tailwind CSS/UI + Pinia + Vue Router'
+        msg: ''
       }
-    },{
+    },
+    {
       path: '/textToaudio',
       name: 'textToaudio',
       component: () => import('@/views/textToaudio/index.vue'),
       props: {
-        msg: 'Hello Vue 3 + TypeScript + Vite + Tailwind CSS/UI + Pinia + Vue Router'
+        msg: ''
       }
-    },{
+    },
+    {
       path: '/message',
       name: 'message',
       component: () => import('@/views/message/index.vue'),
       props: {
-        msg: 'Hello Vue 3 + TypeScript + Vite + Tailwind CSS/UI + Pinia + Vue Router'
+        msg: ''
       }
-    },{
+    },
+    {
       path: '/topicNavigation',
       name: 'topicNavigation',
       component: () => import('@/views/topicNavigation/index.vue'),
       props: {
-        msg: 'Hello Vue 3 + TypeScript + Vite + Tailwind CSS/UI + Pinia + Vue Router'
+        msg: ''
       }
     }
   ]

+ 19 - 2
src/store/main.ts

@@ -1,4 +1,5 @@
 import { defineStore } from 'pinia'
+import { getInfo } from '@/api/module/editor'
 // import { watch } from 'vue'
 
 // useStore could be anything like useUser, useCart
@@ -26,7 +27,10 @@ export const useMainStore = defineStore('main', {
           position: 'left'
         },
         age: 18
-      }
+      },
+      sceneInfo: {},
+      aiSetting: [],
+      navigation: []
     }
   },
   getters: {
@@ -46,7 +50,15 @@ export const useMainStore = defineStore('main', {
     },
     count: ({ counter }) => counter,
     getBasicConfig: ({ basicConfig }) => basicConfig,
-    getDigitalHumanList: ({ digitalHumanList }) => digitalHumanList
+    getDigitalHumanList: ({ digitalHumanList }) => digitalHumanList,
+    getEditorData: ({ basicConfig, aiSetting, navigation, sceneCode }) => {
+      return {
+        baseSetting: basicConfig,
+        navigation: navigation,
+        aiSetting: aiSetting,
+        num: sceneCode
+      }
+    }
   },
   actions: {
     incrementCounter(count: number) {
@@ -72,6 +84,11 @@ export const useMainStore = defineStore('main', {
     },
     setSceneCode(m: string) {
       this.sceneCode = m
+    },
+    async getSceneInfo() {
+      const data = await getInfo(this.sceneCode)
+      console.log('data', data)
+      this.sceneInfo = data as any as {}
     }
   }
 })

+ 12 - 14
src/views/basicSettings/index.vue

@@ -2,18 +2,19 @@
   <div class="base-setting">
     <!-- 基础设置页面 -->
     <n-drawer
-      v-model:show="active"
+      v-model:show="show"
       :width="240"
       placement="right"
       :trap-focus="false"
       :block-scroll="false"
       :show-mask="false"
+      :mask-closable="false"
       to="#drawer-target"
       style="--n-body-padding: 0px"
     >
       <n-drawer-content title="基础设置">
         <div class="drawerContent m-5">
-          <div class="text-lg my-2.5" @click="active = false">基础设置</div>
+          <!--          <div class="text-lg my-2.5" @click="active = false">基础设置</div>-->
           <n-radio-group
             v-model:value="basicConfig.mode"
             name="radiobuttongroup1"
@@ -71,14 +72,14 @@ defineProps<{ msg: string }>()
 
 const main = useMainStore()
 const { setWidthSceneRef } = main
-const active = ref(true)
+const show = ref(true)
 
 const basicConfig = computed(() => {
   return main.basicConfig
 })
 
 // const message = useMessage()
-const fileList = ref([])
+
 const options = [
   {
     label: '默认',
@@ -125,13 +126,6 @@ watch(
   }
 )
 
-watch(
-  active,
-  (newValue) => {
-    // setWidthSceneRef(newValue ? 240 : 0)
-  },
-  { immediate: true, deep: true }
-)
 function handleChange(file, event) {
   console.log(event)
   // message.success((event?.target).response)
@@ -140,11 +134,15 @@ function handleChange(file, event) {
   file.url = '__HTTPS__://www.mocky.io/v2/5e4bafc63100007100d8b70f'
   return file
 }
+
 function changeActive() {
-  console.log('changeActive', active.value)
-  active.value = active.value ? false : true
-  console.log('changeActive', active.value)
+  console.warn('changeActive')
+  //TODO unkown action
+  // console.log('changeActive', active.value)
+  // active.value = active.value ? false : true
+  // console.log('changeActive', active.value)
 }
+
 onMounted(() => {
   // active.value = true
 })

+ 161 - 0
src/views/basicSettings/index1.vue

@@ -0,0 +1,161 @@
+<template>
+  <n-drawer
+    v-model:show="show"
+    :width="340"
+    placement="right"
+    :trap-focus="false"
+    :block-scroll="false"
+    :show-mask="false"
+    :mask-closable="false"
+    to="#drawer-target"
+    style="--n-body-padding: 0px"
+  >
+    <n-drawer-content title="留言列表">
+      <n-list
+        hoverable
+        clickable
+        style="--n-color-modal: none"
+        :show-divider="false"
+      >
+        <n-list-item
+          v-for="(ms, index) in messageList"
+          :key="index"
+          style=""
+          @click="handleMessageClick(ms)"
+        >
+          <n-flex justify="space-between">
+            {{ ms.content.content }}
+
+            <n-dropdown
+              trigger="hover"
+              :options="ms.status === 0 ? fullOptions : delOptions"
+              @select="(key) => handleSelect(key, ms)"
+            >
+              <n-icon :size="20">
+                <svg
+                  xmlns="http://www.w3.org/2000/svg"
+                  xmlns:xlink="http://www.w3.org/1999/xlink"
+                  viewBox="0 0 32 32"
+                >
+                  <circle cx="8" cy="16" r="2" fill="currentColor"></circle>
+                  <circle cx="16" cy="16" r="2" fill="currentColor"></circle>
+                  <circle cx="24" cy="16" r="2" fill="currentColor"></circle>
+                </svg>
+              </n-icon>
+            </n-dropdown>
+          </n-flex>
+        </n-list-item>
+      </n-list>
+    </n-drawer-content>
+  </n-drawer>
+</template>
+
+<script setup lang="ts">
+import { computed, onBeforeMount, onMounted, ref } from 'vue'
+import { useMainStore } from '@/store'
+import { NList, NListItem, NDropdown, useDialog, useMessage } from 'naive-ui'
+import {
+  fetchMessageList,
+  auditMessage,
+  delMessage
+} from '@/api/module/message'
+import type { MessageItem } from './type'
+import { sdk } from '@/sdk'
+
+const show = ref(true)
+defineProps<{ msg: string }>()
+
+const messageList = ref<MessageItem[]>([])
+const dialog = useDialog()
+const message = useMessage()
+const main = useMainStore()
+onBeforeMount(() => {
+  main.setWidthSceneRef(340)
+})
+
+onMounted(() => {
+  show.value = true
+  refresh()
+
+  // sdk.then((sdk) => {
+  //
+  // })
+})
+
+const refresh = async () => {
+  const res = await fetchMessageList(main.sceneCode)
+  console.log('res', res)
+  messageList.value = (res as never as MessageItem[]) || []
+}
+const handleMessageClick = async (ms: MessageItem) => {
+  sdk.then((sdk) => {
+    sdk.TagManager.openTag(ms.content.sid) // 打开热点
+  })
+}
+const fullOptions = ref([
+  {
+    label: '审核并屏示',
+    key: '1'
+  },
+  {
+    label: '删除',
+    key: '2'
+  }
+])
+const delOptions = ref([
+  {
+    label: '删除',
+    key: '2'
+  }
+])
+
+const handleSelect = async (key: string, ms: MessageItem) => {
+  console.log('handleSelect', key, ms.id)
+  switch (key) {
+    case '1':
+      await auditMessage({
+        id: Number(ms.id),
+        num: main.sceneCode
+      })
+      message.success('审核成功!')
+      await refresh()
+      break
+    case '2':
+      dialog.warning({
+        title: '删除',
+        content: '是否确定删除?',
+        negativeText: '取消',
+        positiveText: '确认',
+        onPositiveClick: async () => {
+          await delMessage({
+            id: Number(ms.id),
+            num: main.sceneCode
+          })
+          sdk.then((sdk) => {
+            sdk.TagManager.removeTag(ms.content.sid) // 打开热点
+          })
+          await refresh()
+        }
+      })
+      break
+  }
+}
+
+// __sdk.TagManager.openTag('1730448544227') // 打开热点
+// __sdk.TagManager.removeTag('1730448544227') // 移除热点
+</script>
+
+<style lang="sass" scoped>
+a
+  color: #42b983
+
+label
+  margin: 0 0.5em
+  font-weight: bold
+
+code
+  background-color: #eee
+  padding: 2px 4px
+  border-radius: 4px
+  color: #304455
+</style>

+ 40 - 40
src/views/digitalHuman/index.vue

@@ -26,46 +26,46 @@
             </div>
             <div class="list my-2">
               <div class="listItem">
-                <div class="3D flex justify-between justify-items-center">
-                  <span>
-                    <n-icon size="17" color="#000">
-                      <LogoAppleAr class="relative top-0.5" />
-                    </n-icon>
-                    企业荣誉
-                  </span>
-                  <n-popover placement="bottom" trigger="click">
-                    <template #trigger>
-                      <n-icon size="17" color="#000">
-                        <EllipsisHorizontal />
-                      </n-icon>
-                    </template>
-                    <div class="butList w-12 pointer-events-auto">
-                      <div class="butItem">编辑</div>
-                      <div class="butItem">移动</div>
-                      <div class="butItem">删除</div>
-                    </div>
-                  </n-popover>
-                </div>
-                <div class="video flex justify-between justify-items-center">
-                  <span>
-                    <n-icon size="17" color="#000">
-                      <Videocam class="relative top-0.5" />
-                    </n-icon>
-                    企业荣誉
-                  </span>
-                  <n-popover placement="bottom" trigger="click">
-                    <template #trigger>
-                      <n-icon size="17" color="#000">
-                        <EllipsisHorizontal />
-                      </n-icon>
-                    </template>
-                    <div class="butList w-12 pointer-events-auto">
-                      <div class="butItem">编辑</div>
-                      <div class="butItem">移动</div>
-                      <div class="butItem">删除</div>
-                    </div>
-                  </n-popover>
-                </div>
+<!--                <div class="3D flex justify-between justify-items-center">-->
+<!--                  <span>-->
+<!--                    <n-icon size="17" color="#000">-->
+<!--                      <LogoAppleAr class="relative top-0.5" />-->
+<!--                    </n-icon>-->
+<!--                    企业荣誉-->
+<!--                  </span>-->
+<!--                  <n-popover placement="bottom" trigger="click">-->
+<!--                    <template #trigger>-->
+<!--                      <n-icon size="17" color="#000">-->
+<!--                        <EllipsisHorizontal />-->
+<!--                      </n-icon>-->
+<!--                    </template>-->
+<!--                    <div class="butList w-12 pointer-events-auto">-->
+<!--                      <div class="butItem">编辑</div>-->
+<!--                      <div class="butItem">移动</div>-->
+<!--                      <div class="butItem">删除</div>-->
+<!--                    </div>-->
+<!--                  </n-popover>-->
+<!--                </div>-->
+<!--                <div class="video flex justify-between justify-items-center">-->
+<!--                  <span>-->
+<!--                    <n-icon size="17" color="#000">-->
+<!--                      <Videocam class="relative top-0.5" />-->
+<!--                    </n-icon>-->
+<!--                    企业荣誉-->
+<!--                  </span>-->
+<!--                  <n-popover placement="bottom" trigger="click">-->
+<!--                    <template #trigger>-->
+<!--                      <n-icon size="17" color="#000">-->
+<!--                        <EllipsisHorizontal />-->
+<!--                      </n-icon>-->
+<!--                    </template>-->
+<!--                    <div class="butList w-12 pointer-events-auto">-->
+<!--                      <div class="butItem">编辑</div>-->
+<!--                      <div class="butItem">移动</div>-->
+<!--                      <div class="butItem">删除</div>-->
+<!--                    </div>-->
+<!--                  </n-popover>-->
+<!--                </div>-->
               </div>
             </div>
           </div>

+ 4 - 5
src/views/message/index.vue

@@ -1,5 +1,4 @@
 <template>
-  <div>留言互动</div>
   <n-drawer
     v-model:show="show"
     :width="340"
@@ -29,7 +28,7 @@
 
             <n-dropdown
               trigger="hover"
-              :options="ms.status === 0 ? fullPptions : delPptions"
+              :options="ms.status === 0 ? fullOptions : delOptions"
               @select="(key) => handleSelect(key, ms)"
             >
               <n-icon :size="20">
@@ -52,7 +51,7 @@
 </template>
 
 <script setup lang="ts">
-import { computed, onBeforeMount, onMounted, ref, watch } from 'vue'
+import { computed, onBeforeMount, onMounted, ref } from 'vue'
 import { useMainStore } from '@/store'
 import { NList, NListItem, NDropdown, useDialog, useMessage } from 'naive-ui'
 import {
@@ -93,7 +92,7 @@ const handleMessageClick = async (ms: MessageItem) => {
     sdk.TagManager.openTag(ms.content.sid) // 打开热点
   })
 }
-const fullPptions = ref([
+const fullOptions = ref([
   {
     label: '审核并屏示',
     key: '1'
@@ -103,7 +102,7 @@ const fullPptions = ref([
     key: '2'
   }
 ])
-const delPptions = ref([
+const delOptions = ref([
   {
     label: '删除',
     key: '2'

+ 96 - 88
src/views/topicNavigation/index.vue

@@ -12,63 +12,43 @@
       style="--n-body-padding: 0px"
     >
       <n-drawer-content title="专题导航">
-        <div class="drawerContent m-5">
-          <div class="text-lg my-2.5">专题导航</div>
-          <div class="list" v-if="show">
-            <div class="butlist text-center pb-2">
-              <n-button type="primary" @click="handleShow">+ 添加</n-button>
-            </div>
-            <div class="list my-2">
-              <div class="listItem">
-                <div class="video flex justify-between justify-items-center">
-                  <span>
-                    <n-icon size="17" color="#000">
-                      <WalkSharp class="relative top-0.5" />
-                    </n-icon>
-                    企业荣誉
-                  </span>
-                  <n-popover placement="bottom" trigger="click">
-                    <template #trigger>
-                      <n-icon size="17" color="#000">
-                        <EllipsisHorizontal />
-                      </n-icon>
-                    </template>
-                    <div class="butList w-12 pointer-events-auto">
-                      <div class="butItem">编辑</div>
-                      <div class="butItem">删除</div>
-                    </div>
-                  </n-popover>
-                </div>
-              </div>
-            </div>
-          </div>
-          <div v-else class="showList">
-            <div class="doclist">
-              <div
-                class="docItem flex justify-between py-1"
-                v-for="(item, index) in doclist"
-                :key="index"
-              >
-                <n-radio
-                  :checked="item.checked"
-                  :value="item.name"
-                  :name="item.name"
-                  @click="handleChange(item)"
-                >
-                  {{ item.name }}
-                </n-radio>
-                <n-icon @click="handleConfirm" size="17" color="red">
-                  <TrashOutline />
+        <div class="drawerContent m-5"></div>
+        <n-flex justify="center">
+          <n-button type="primary" @click="handleAdd">+ 添加</n-button>
+        </n-flex>
+
+        <n-list
+          hoverable
+          clickable
+          style="--n-color-modal: none; margin-top: 20px"
+          :show-divider="false"
+          @select="handleSelect"
+        >
+          <n-list-item v-for="(nav, index) in dataList" :key="index" style="">
+            <n-flex justify="space-between">
+              <n-flex align="center">
+                <n-icon size="20">
+                  <WalkSharp />
+                </n-icon>
+                {{ nav.title }}
+              </n-flex>
+
+              <n-dropdown trigger="hover" :options="fullOptions">
+                <n-icon :size="20">
+                  <svg
+                    xmlns="http://www.w3.org/2000/svg"
+                    xmlns:xlink="http://www.w3.org/1999/xlink"
+                    viewBox="0 0 32 32"
+                  >
+                    <circle cx="8" cy="16" r="2" fill="currentColor"></circle>
+                    <circle cx="16" cy="16" r="2" fill="currentColor"></circle>
+                    <circle cx="24" cy="16" r="2" fill="currentColor"></circle>
+                  </svg>
                 </n-icon>
-              </div>
-              <div class="backBut text-center mt-2">
-                <n-button type="primary" @click="handleShow"
-                  >确认并返回列表</n-button
-                >
-              </div>
-            </div>
-          </div>
-        </div>
+              </n-dropdown>
+            </n-flex>
+          </n-list-item>
+        </n-list>
       </n-drawer-content>
     </n-drawer>
   </div>
@@ -76,24 +56,60 @@
 
 <script setup lang="ts">
 import { ref } from 'vue'
-import { sdk,clearScreen } from '@/sdk'
+import { sdk, clearScreen } from '@/sdk'
 import { onMounted, onUnmounted } from 'vue'
-import { NRadio, NPopover, useDialog, useMessage } from 'naive-ui'
+import {
+  NRadio,
+  NPopover,
+  useDialog,
+  useMessage,
+  NDropdown,
+  NList,
+  NListItem
+} from 'naive-ui'
 import { WalkSharp, TrashOutline, EllipsisHorizontal } from '@vicons/ionicons5'
 
 const panos = ref([])
+const isEditing = ref({
+  index: NaN
+})
+
+const dataList = ref<
+  {
+    title: string
+    panos: string[]
+  }[]
+>([])
 
 sdk.then((sdk) => {
   sdk.PanoCheckManager.on('changed', (list: any) => {
     panos.value = list
-    console.log(panos.value)
+    console.log('panos', panos.value)
   })
 })
 
+const handleAdd = () => {
+  sdk.then((sdk) => {
+    sdk.Scene.whenLoaded(() => {
+      sdk.PanoCheckManager.enter()
+      clearScreen(true)
+    })
+  })
+  if (panos.value.length === 0) {
+    dataList.value.push({
+      title: '新增路线',
+      panos: []
+    })
+  } else {
+    // message.warning('请先保存数据!')
+  }
+}
+
 onMounted(() => {
   sdk.then((sdk) => {
     sdk.Scene.whenLoaded(() => {
       sdk.PanoCheckManager.enter()
+      // debugger
       clearScreen(true)
     })
   })
@@ -107,39 +123,31 @@ onUnmounted(() => {
 })
 
 defineProps<{ msg: string }>()
+
 const dialog = useDialog()
 const message = useMessage()
+
 const active = ref(true)
-const show = ref(true)
-const doclist = ref([
-  { name: 'test1', checked: false },
-  { name: 'test2', checked: false },
-  { name: 'test3', checked: false },
-  { name: 'test4', checked: false },
-  { name: 'test5', checked: false },
-  { name: 'test6', checked: false },
-  { name: 'test7', checked: false }
+// const show = ref(true)
+
+const fullOptions = ref([
+  {
+    label: '编辑',
+    key: '1'
+  },
+  {
+    label: '删除',
+    key: '2'
+  }
 ])
-const handleShow = () => {
-  show.value = !show.value
-}
-const handleConfirm = () => {
-  dialog.warning({
-    title: '提示',
-    content: '确认删除此点位?',
-    positiveText: '确定',
-    negativeText: '取消',
-    onPositiveClick: () => {
-      message.success('确定')
-    },
-    onNegativeClick: () => {
-      message.error('不确定')
-    }
-  })
-}
-const handleChange = (item) => {
-  console.log(item, 'handleChange')
-  item.checked = !item.checked
+
+const handleSelect = (key: string) => {
+  switch (key) {
+    case '1':
+      break
+    case '2':
+      break
+  }
 }
 </script>