Browse Source

Merge branch 'dev' of http://1.94.207.143:3000/xyy/xyy-m into dev

# Conflicts:
#	src/middleware/auth.global.js
songzhen 3 months ago
parent
commit
c72cbc5a1d
31 changed files with 1058 additions and 1111 deletions
  1. 1 0
      package.json
  2. 21 0
      pnpm-lock.yaml
  3. 8 0
      src/assets/img/note-create/close-circle.svg
  4. 5 0
      src/assets/img/note-create/close.svg
  5. 10 0
      src/assets/img/note-create/draft.svg
  6. 4 0
      src/assets/img/note-create/image.svg
  7. 1 0
      src/assets/img/note-create/left.svg
  8. 13 0
      src/assets/img/note-create/rate.svg
  9. 0 27
      src/components/CreateNote/BottomActions.vue
  10. 28 20
      src/components/CreateNote/Form.vue
  11. 42 25
      src/components/CreateNote/HeaderBanner.vue
  12. 0 55
      src/components/CreateNote/InsertContentSection.vue
  13. 0 70
      src/components/CreateNote/InsertImageModal.vue
  14. 0 37
      src/components/CreateNote/InsertImageSection.vue
  15. 0 60
      src/components/CreateNote/InsertTitleModal.client.vue
  16. 0 50
      src/components/CreateNote/InsertTitleSection.vue
  17. 0 58
      src/components/CreateNote/LeftActions.vue
  18. 0 153
      src/components/CreateNote/PreviewModal.vue
  19. 34 28
      src/components/CreateNote/PublishResultModal.client.vue
  20. 122 59
      src/components/CreateNote/UserInfoModal.client.vue
  21. 6 11
      src/components/Profile/Notes/Auditing/Item.vue
  22. 30 25
      src/components/Profile/Notes/Auditing/index.vue
  23. 13 17
      src/components/Profile/Notes/Draft/Item.vue
  24. 23 17
      src/components/Profile/Notes/Draft/index.vue
  25. 13 5
      src/components/Profile/Notes/Published/Item.vue
  26. 110 35
      src/components/Profile/Notes/Published/index.vue
  27. 6 19
      src/components/Profile/Notes/Rejected/Item.vue
  28. 26 22
      src/components/Profile/Notes/Rejected/index.vue
  29. 24 0
      src/middleware/create-note.global.js
  30. 515 315
      src/pages/note-create/index.client.vue
  31. 3 3
      src/pages/profile/notes/index.vue

+ 1 - 0
package.json

@@ -24,6 +24,7 @@
     "nuxt-swiper": "^2.0.0",
     "pinia": "^2.2.2",
     "vue": "latest",
+    "vue-draggable-plus": "^0.6.0",
     "vue-router": "latest"
   },
   "devDependencies": {

+ 21 - 0
pnpm-lock.yaml

@@ -41,6 +41,9 @@ importers:
       vue:
         specifier: latest
         version: 3.5.10
+      vue-draggable-plus:
+        specifier: ^0.6.0
+        version: 0.6.0(@types/sortablejs@1.15.8)
       vue-router:
         specifier: latest
         version: 4.4.5(vue@3.5.10)
@@ -1133,6 +1136,9 @@ packages:
   '@types/resolve@1.20.2':
     resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
 
+  '@types/sortablejs@1.15.8':
+    resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==}
+
   '@types/web-bluetooth@0.0.20':
     resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
 
@@ -3616,6 +3622,15 @@ packages:
   vue-devtools-stub@0.1.0:
     resolution: {integrity: sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==}
 
+  vue-draggable-plus@0.6.0:
+    resolution: {integrity: sha512-G5TSfHrt9tX9EjdG49InoFJbt2NYk0h3kgjgKxkFWr3ulIUays0oFObr5KZ8qzD4+QnhtALiRwIqY6qul4egqw==}
+    peerDependencies:
+      '@types/sortablejs': ^1.15.0
+      '@vue/composition-api': '*'
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+
   vue-router@4.4.5:
     resolution: {integrity: sha512-4fKZygS8cH1yCyuabAXGUAsyi1b2/o/OKgu/RUb+znIYOxPRxdkytJEx+0wGcpBE1pX6vUgh5jwWOKRGvuA/7Q==}
     peerDependencies:
@@ -4824,6 +4839,8 @@ snapshots:
 
   '@types/resolve@1.20.2': {}
 
+  '@types/sortablejs@1.15.8': {}
+
   '@types/web-bluetooth@0.0.20': {}
 
   '@unhead/dom@1.11.6':
@@ -7626,6 +7643,10 @@ snapshots:
 
   vue-devtools-stub@0.1.0: {}
 
+  vue-draggable-plus@0.6.0(@types/sortablejs@1.15.8):
+    dependencies:
+      '@types/sortablejs': 1.15.8
+
   vue-router@4.4.5(vue@3.5.10):
     dependencies:
       '@vue/devtools-api': 6.6.4

+ 8 - 0
src/assets/img/note-create/close-circle.svg

@@ -0,0 +1,8 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="close-circle">
+<g id="union">
+<path d="M12.4524 25.7797L18.2322 19.9999L12.4526 14.2202L14.2204 12.4525L20 18.2321L25.7796 12.4525L27.5474 14.2202L21.7678 19.9999L27.5476 25.7797L25.7799 27.5475L20 21.7676L14.2201 27.5475L12.4524 25.7797Z" fill="white"/>
+<path d="M20 2.5C29.665 2.5 37.5 10.335 37.5 20C37.5 29.665 29.665 37.5 20 37.5C10.335 37.5 2.49999 29.665 2.5 20C2.5 10.335 10.335 2.49999 20 2.5ZM20 5C11.7157 5 5 11.7157 5 20C5 28.2843 11.7157 35 20 35C28.2843 35 35 28.2843 35 20C35 11.7157 28.2843 5 20 5Z" fill="white"/>
+</g>
+</g>
+</svg>

+ 5 - 0
src/assets/img/note-create/close.svg

@@ -0,0 +1,5 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="close">
+<path id="union" d="M16 17.8407L22.1593 23.9999L24 22.1592L17.8407 16L24 9.84075L22.1593 8.00005L16 14.1593L9.84071 8L8 9.8407L14.1593 16L8 22.1593L9.84071 24L16 17.8407Z" fill="black" fill-opacity="0.9"/>
+</g>
+</svg>

+ 10 - 0
src/assets/img/note-create/draft.svg

@@ -0,0 +1,10 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="icon">
+<g id="file-copy">
+<g id="union">
+<path d="M6.49993 3.64263C6.49993 2.83874 7.21501 2.375 7.86853 2.375H13.878C14.2473 2.375 14.601 2.5235 14.8596 2.78707L19.8567 7.8807C20.1086 8.13753 20.2499 8.48288 20.2501 8.84266L20.2563 17.6069C20.2563 18.4108 19.5413 18.875 18.8877 18.875H7.87493C7.22149 18.875 6.50652 18.4114 6.50634 17.6077L6.49993 3.64263ZM7.87498 3.75L7.88129 17.5H18.8813L18.8754 9.26754H13.375V3.75H7.87498ZM14.75 4.63887V7.89254H17.9421L14.75 4.63887Z" fill="#333333"/>
+<path d="M3.75 7.87502H5.125V20.2675L16.125 20.2675V21.6425H5.125C4.36561 21.6425 3.75 21.0269 3.75 20.2675V7.87502Z" fill="#333333"/>
+</g>
+</g>
+</g>
+</svg>

File diff suppressed because it is too large
+ 4 - 0
src/assets/img/note-create/image.svg


+ 1 - 0
src/assets/img/note-create/left.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="12.156" height="10.898" viewBox="0 0 12.156 10.898"><defs><style>.a,.b{fill:none;stroke:#fd9a00;stroke-linecap:round;}.a{opacity:0.48;}</style></defs><g transform="translate(-334.657 -204.554)"><path class="a" d="M2322.106-1175.739l-4.742,4.742,4.742,4.742" transform="translate(-1976 1381)"/><path class="b" d="M2322.106-1175.739l-4.742,4.742,4.742,4.742" transform="translate(-1982 1381)"/></g></svg>

+ 13 - 0
src/assets/img/note-create/rate.svg

@@ -0,0 +1,13 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="item/rate/24">
+<g id="star-filled">
+<path id="union" d="M11.3964 2.72649C11.644 2.2248 12.3594 2.2248 12.607 2.72649L15.308 8.19931L21.3476 9.07692C21.9013 9.15736 22.1224 9.83775 21.7217 10.2283L17.3513 14.4882L18.383 20.5034C18.4777 21.0549 17.8988 21.4754 17.4037 21.215L12.0017 18.375L6.59973 21.215C6.10453 21.4754 5.52576 21.0549 5.62034 20.5035L6.65203 14.4882L2.28173 10.2283C1.88111 9.83775 2.10218 9.15736 2.65583 9.07692L8.69543 8.19931L11.3964 2.72649Z" fill="url(#paint0_linear_766_6128)"/>
+</g>
+</g>
+<defs>
+<linearGradient id="paint0_linear_766_6128" x1="8.57534" y1="2.66391" x2="19.7399" y2="12.5938" gradientUnits="userSpaceOnUse">
+<stop stop-color="#FFE590"/>
+<stop offset="1" stop-color="#FD9A00"/>
+</linearGradient>
+</defs>
+</svg>

+ 0 - 27
src/components/CreateNote/BottomActions.vue

@@ -1,27 +0,0 @@
-<template>
-  <div class="mx-auto flex w-wrap justify-center space-x-20">
-    <div
-      @click="$emit('onPreview')"
-      class="flex h-60 w-200 cursor-pointer items-center justify-center rounded-full border border-primary bg-white text-2xl font-semibold text-primary hover:opacity-80"
-    >
-      预览
-    </div>
-    <el-button
-      @click="$emit('onPublish')"
-      :loading="publishLoading"
-      type="primary"
-      size="large"
-      style="width: 200px; height: 60px; font-size: 18px; border-radius: 30px"
-      >发表游记</el-button
-    >
-  </div>
-</template>
-
-<script setup>
-defineProps({
-  publishLoading: Boolean
-})
-defineEmits(['onPreview', 'onPublish'])
-</script>
-
-<style lang="scss" scoped></style>

+ 28 - 20
src/components/CreateNote/Form.vue

@@ -94,6 +94,7 @@
       <template #input>
         <van-rate
           color="#ffd21e"
+          :icon="rate"
           void-icon="star"
           void-color="#eee"
           v-model="recommendationRate"
@@ -145,6 +146,22 @@
 
 <script setup>
 import calendar from '~/assets//img/note-create/calendar.png'
+import rate from '~/assets//img/note-create/rate.svg'
+
+const departureTime = defineModel('departureTime')
+const countTimes = defineModel('countTimes')
+const endPlace = defineModel('endPlace')
+const role = defineModel('role')
+const averageCost = defineModel('averageCost')
+const recommendationRate = defineModel('recommendationRate')
+const travelModeId = defineModel('travelMode')
+const travelNumber = defineModel('travelNumber')
+
+onMounted(() => {
+  getPlaceOptions()
+  getTravelModeOptions()
+})
+
 const labelWidth = '68px'
 // 出发时间
 const minDate = new Date(2010, 0, 1)
@@ -154,12 +171,19 @@ const showCalendar = ref(false)
 
 // 展示时间的格式
 const onConfirm = (date) => {
-  departureTime.value = date
-    ? `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`
-    : ''
+  departureTime.value = date ? `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}` : ''
   showCalendar.value = false
 }
 
+let travelMode = computed(() => {
+  if (travelModeId.value) {
+    let travelModeOptionsItem = travelModeOptions.value.find((el) => el.id == travelModeId?.value)
+    return travelModeOptionsItem?.name
+  } else {
+    return ''
+  }
+})
+
 // 目的地
 const showPicker = ref(false)
 const onConfirmAddr = ({ selectedValues, selectedOptions }) => {
@@ -171,23 +195,9 @@ const onConfirmAddr = ({ selectedValues, selectedOptions }) => {
 const showtravelMode = ref(false)
 const onConfirmTravelMode = ({ selectedValues, selectedOptions }) => {
   showtravelMode.value = false
-  travelMode.value = selectedOptions[selectedValues.length - 1].name
+  travelModeId.value = selectedOptions[selectedValues.length - 1].id
 }
 
-const departureTime = defineModel('departureTime')
-const countTimes = defineModel('countTimes')
-const endPlace = defineModel('endPlace')
-const role = defineModel('role')
-const averageCost = defineModel('averageCost')
-const recommendationRate = defineModel('recommendationRate')
-const travelMode = defineModel('travelMode')
-const travelNumber = defineModel('travelNumber')
-
-onMounted(() => {
-  getPlaceOptions()
-  getTravelModeOptions()
-})
-
 const placeOptionProps = {
   text: 'menuName',
   value: 'id',
@@ -211,8 +221,6 @@ async function getPlaceOptions() {
 const travelModeOptions = ref([])
 async function getTravelModeOptions() {
   const { data } = await request('/admin/app/extra/listDict?dictCode=TourTravelMode')
-  console.log(data, '115')
-
   travelModeOptions.value = data
 }
 </script>

+ 42 - 25
src/components/CreateNote/HeaderBanner.vue

@@ -1,14 +1,9 @@
 <template>
   <div style="height: calc(100vw * 0.59)" class="w-full">
-    <!-- v-if="!bannerUrl" -->
     <div
+      v-if="!bannerUrl"
       class="flex h-full w-full items-center justify-center bg-[url('~/assets/img/note-create/note_create_banner_bg.png')]"
     >
-      <!-- <van-uploader    reupload  v-model="fileList" max-count="1" :preview-size="['100%', 221]">
-    </van-uploader> -->
-      <!-- <van-button icon="plus" type="primary">上传文件</van-button> -->
-
-      <!-- <input type="file" class="van-uploader__input" value="1213" accept="image/*"> -->
       <div
         @click="handleSelectImage"
         class="flex h-40 w-150 active:bg-[#000]/[0.1] bg-[#FFFFFF]/[0.2] items-center justify-center space-x-10 rounded-full border-2 border-white"
@@ -16,28 +11,29 @@
         <div class="w-16 h-16">
           <van-image width="100%" height="100%" :src="icon_image_fill" />
         </div>
-        <!-- <van-uploader v-model="fileList" multiple :max-count="1" /> -->
-
         <span class="text-sm font-normal pt-4 text-[#FF9300]">设置游记头图</span>
       </div>
     </div>
 
-    <!-- <div v-else class="relative h-full w-full">
+    <div v-else class="relative h-full w-full">
       <img :src="bannerUrl" class="h-full w-full" alt="" />
       <div
         @click="open"
-        class="absolute bottom-0 left-0 flex cursor-pointer items-center space-x-8 bg-[#00000080] px-15 py-8 hover:opacity-90"
+        class="absolute rounded-full bottom-10 right-10 flex cursor-pointer items-center space-x-8 bg-[#00000080] px-15 py-8 hover:opacity-90"
       >
-        <span
-          class="iconfont icon-setting text-primary"
-          style="font-size: 20px"
-        ></span>
-        <span class="text-base text-primary">重新上传头图</span>
+        <div class="w-16 h-16">
+          <van-image width="100%" height="100%" :src="image" />
+        </div>
+
+        <span class="text-base text-white">重新上传头图</span>
       </div>
-    </div> -->
-    <!-- <el-dialog title="图片剪裁" v-model="cropperDialogVisible" width="1000px">
-      <div class="h-380 w-full">
-        <vueCropper
+    </div>
+
+    <!-- style="background-color: transparent" -->
+    <van-dialog title="图片剪裁" v-model:show="cropperDialogVisible" width="375">
+      <div class="h-full w-full">
+        <img ref="cropperRef" :src="bannerUrl" alt="" />
+        <!-- <vueCropper
           ref="cropperRef"
           :img="cropperImageBase64"
           autoCrop
@@ -47,23 +43,36 @@
           fixedBox
           :full="true"
           :fixedNumber="[3.2, 1]"
-        ></vueCropper>
+        ></vueCropper> -->
+        <!-- <vue-cropper
+          v-if="image"
+          :src="image"
+          ref="cropper"
+          :stencil-size="stencilSize"
+          :zoomable="true"
+          :rotatable="true"
+          :background="true"
+          :auto-crop="true"
+          :auto-crop-area="0.8"
+          :guides="true"
+          :center="true"
+          @crop="onCrop"
+        /> -->
       </div>
       <template #footer>
         <div class="dialog-footer">
           <el-button @click="cropperDialogVisible = false">取消</el-button>
-          <el-button type="primary" @click="handleCropperOk" :loading="loading">
-            确认
-          </el-button>
+          <el-button type="primary" @click="handleCropperOk" :loading="loading">确认</el-button>
         </div>
       </template>
-    </el-dialog> -->
+    </van-dialog>
   </div>
 </template>
 
 <script setup>
 import { useFileDialog } from '@vueuse/core'
 import icon_image_fill from '~/assets/img/note-create/icon-image-fill.png'
+import image from '~/assets/img/note-create/image.svg'
 // import { VueCropper } from 'vue-cropper'
 // import 'vue-cropper/dist/index.css'
 
@@ -78,12 +87,15 @@ const { open, onChange } = useFileDialog({
 const cropperImageBase64 = ref('')
 const fileList = ref([])
 onChange((files) => {
+  console.log(files[0], '4445')
+
   if (!files.length) return
   const reader = new FileReader()
   reader.readAsDataURL(files[0])
   reader.onload = () => {
     cropperDialogVisible.value = true
     cropperImageBase64.value = reader.result
+    // bannerUrl.value = reader.result
   }
 })
 
@@ -94,12 +106,17 @@ function handleSelectImage() {
 const cropperRef = ref(null)
 const cropperDialogVisible = ref(false)
 const loading = ref(false)
+// async
+
 async function handleCropperOk() {
   cropperRef.value?.getCropBlob(async (data) => {
     try {
-      loading.value = true
+      // loading.value = true
       // 此处需上传图片,保存URL
       const formData = new FormData()
+      console.log(formData, '121222')
+      console.log(data, '5555')
+
       formData.append('uploadFile', data)
       formData.append('asImage', true)
       formData.append('fieldName', 'travelNotesBanner')

+ 0 - 55
src/components/CreateNote/InsertContentSection.vue

@@ -1,55 +0,0 @@
-<template>
-  <div
-    ref="target"
-    class="relative overflow-hidden rounded-lg border border-dashed border-primary bg-white px-10"
-  >
-    <textarea
-      class="min-h-200 w-full resize-none rounded-lg border-none py-10 text-base leading-[28px] text-black-3 outline-none"
-      ref="textarea"
-      v-model="input"
-      autofocus
-      placeholder="从这里开始游记正文..."
-    />
-    <div
-      v-show="!isOutside"
-      class="absolute right-0 top-0 flex h-26 items-center justify-center space-x-15 bg-[rgba(0,0,0,0.7)] px-12 transition-all"
-    >
-      <span
-        @click="$emit('onDelete')"
-        class="iconfont icon-delete cursor-pointer text-white"
-        style="font-size: 18px"
-      ></span>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { useMouseInElement } from '@vueuse/core'
-
-const target = ref(null)
-const { isOutside } = useMouseInElement(target)
-
-const model = defineModel()
-
-defineEmits(['onDelete'])
-
-const { textarea, input } = useTextareaAutosize()
-
-watchEffect(() => {
-  input.value = model.value
-})
-watch(input, () => {
-  model.value = input.value
-})
-</script>
-
-<style lang="scss" scoped>
-textarea {
-  -ms-overflow-style: none;
-  scrollbar-width: none;
-}
-
-textarea::-webkit-scrollbar {
-  display: none;
-}
-</style>

+ 0 - 70
src/components/CreateNote/InsertImageModal.vue

@@ -1,70 +0,0 @@
-<template>
-  <div>
-    <el-dialog
-      v-model="visible"
-      title="选择图片"
-      width="820"
-      destroy-on-close
-      :z-index="9999"
-      @closed="onDialogClosed"
-    >
-      <div class="max-h-400 py-20">
-        <el-upload
-          v-model:file-list="fileList"
-          name="uploadFile"
-          :headers="{
-            Authorization: token
-          }"
-          :data="{
-            asImage: true,
-            fieldName: 'tourismTavelNotesUrl'
-          }"
-          multiple
-          :auto-upload="true"
-          :action="uploadUrl"
-          list-type="picture-card"
-        >
-          <span class="iconfont icon-plus" style="font-size: 26px"></span>
-        </el-upload>
-      </div>
-
-      <template #footer>
-        <div class="flex items-center justify-end">
-          <el-button @click="visible = false">取消</el-button>
-          <el-button type="primary" @click="handleOk"> 确定 </el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-const visible = defineModel('visible', false)
-
-const emit = defineEmits(['onOk'])
-
-const useAuth = useAuthStore()
-const { token } = storeToRefs(useAuth)
-
-const uploadUrl = `${import.meta.env.VITE_APP_BASE_URL}/admin/app/tourismProjectTravelNotesWrite/upload`
-
-const fileList = ref([])
-
-function handleOk() {
-  const fileUrlList = fileList.value.map((e) => ({
-    fileUrl: e.response.data.fileUrl
-  }))
-  if (fileUrlList.length === 0) {
-    ElMessage.warning('请选择图片')
-    return
-  }
-  emit('onOk', fileUrlList)
-  visible.value = false
-}
-
-function onDialogClosed() {
-  fileList.value = []
-}
-</script>
-
-<style lang="scss" scoped></style>

+ 0 - 37
src/components/CreateNote/InsertImageSection.vue

@@ -1,37 +0,0 @@
-<template>
-  <div
-    class="relative rounded-lg border border-dashed border-primary px-10 py-10"
-    ref="target"
-  >
-    <img :src="url" class="h-auto w-full" lazy alt="" srcset="" />
-    <div
-      v-show="!isOutside"
-      class="absolute right-0 top-0 flex h-26 items-center justify-center space-x-15 bg-[rgba(0,0,0,0.7)] px-12 transition-all"
-    >
-      <span
-        @click="$emit('onSaveCover')"
-        class="cursor-pointer text-base text-white"
-        >设为封面图</span
-      >
-      <span
-        @click="$emit('onDelete')"
-        class="iconfont icon-delete cursor-pointer text-white"
-        style="font-size: 18px"
-      ></span>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { useMouseInElement } from '@vueuse/core'
-defineProps({
-  url: String
-})
-
-const emit = defineEmits(['onDelete', 'onSaveCover'])
-
-const target = ref(null)
-const { isOutside } = useMouseInElement(target)
-</script>
-
-<style lang="scss" scoped></style>

+ 0 - 60
src/components/CreateNote/InsertTitleModal.client.vue

@@ -1,60 +0,0 @@
-<template>
-  <div>
-    <el-dialog
-      v-model="visible"
-      title="插入标题"
-      width="600"
-      destroy-on-close
-      :z-index="9999"
-      @closed="onDialogClosed"
-    >
-      <div class="py-20">
-        <el-input
-          size="large"
-          maxlength="30"
-          v-model="inputValue"
-          show-word-limit=""
-          placeholder="请输入段落标题"
-        />
-      </div>
-
-      <template #footer>
-        <div class="flex items-center justify-end">
-          <el-button @click="visible = false">取消</el-button>
-          <el-button type="primary" @click="handleOk"> 确定 </el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-const visible = defineModel('visible', false)
-
-const props = defineProps({
-  title: String
-})
-
-const inputValue = ref()
-
-watchEffect(() => {
-  inputValue.value = props.title
-})
-
-const emit = defineEmits(['onOk'])
-
-function handleOk() {
-  if (!inputValue.value) {
-    ElMessage.warning('请输入段落标题')
-    return
-  }
-  visible.value = false
-  emit('onOk', inputValue.value)
-}
-
-function onDialogClosed() {
-  inputValue.value = ''
-}
-</script>
-
-<style lang="scss" scoped></style>

+ 0 - 50
src/components/CreateNote/InsertTitleSection.vue

@@ -1,50 +0,0 @@
-<template>
-  <div
-    class="relative box-border flex min-h-60 items-center justify-between space-x-10 rounded-lg border border-dashed border-primary bg-white px-10 py-10"
-    ref="target"
-  >
-    <van-cell-group class="border" inset>
-      <van-field
-        v-model="title"
-        rows="1"
-        autosize
-        label-width="5"
-        type="textarea"
-        placeholder="请输入段落标题..."
-        maxlength="50"
-        show-word-limit
-      ></van-field>
-    </van-cell-group>
-    <span class="text-3xl text-black-3">{{ title }}</span>
-    <div
-      v-show="!isOutside"
-      class="absolute right-0 top-0 flex h-26 items-center justify-center space-x-15 bg-[rgba(0,0,0,0.7)] px-12 transition-all"
-    >
-      <span
-        @click="$emit('onEdit')"
-        class="iconfont icon-edit-square cursor-pointer text-white"
-        style="font-size: 18px"
-      ></span>
-      <span
-        @click="$emit('onDelete')"
-        class="iconfont icon-delete cursor-pointer text-white"
-        style="font-size: 18px"
-      ></span>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { useMouseInElement } from '@vueuse/core'
-
-const target = ref(null)
-const { isOutside } = useMouseInElement(target)
-
-const props = defineProps({
-  title: String
-})
-
-defineEmits(['onEdit', 'onDelete'])
-</script>
-
-<style lang="scss" scoped></style>

+ 0 - 58
src/components/CreateNote/LeftActions.vue

@@ -1,58 +0,0 @@
-<template>
-  <div class="mt-20 flex flex-col space-y-15 text-xl text-black-6">
-    <div
-      class="flex cursor-pointer items-center space-x-15"
-      @click="$emit('onInsertTitle')"
-    >
-      <img
-        src=" ~/assets/img/note-create/create_note_insert_title.png"
-        class="h-26 w-26"
-        alt=""
-        srcset=""
-      />
-      <span>插入段落标题</span>
-    </div>
-    <div
-      class="flex cursor-pointer items-center space-x-15"
-      @click="$emit('onInsertContent')"
-    >
-      <img
-        src=" ~/assets/img/note-create/create_note_insert_title.png"
-        class="h-26 w-26"
-        alt=""
-        srcset=""
-      />
-      <span>插入段落内容</span>
-    </div>
-    <div
-      class="flex cursor-pointer items-center space-x-15"
-      @click="$emit('onInsertImg')"
-    >
-      <img
-        src=" ~/assets/img/note-create/create_note_insert_img.png"
-        class="h-26 w-26"
-        alt=""
-        srcset=""
-      />
-      <span>插入图片</span>
-    </div>
-    <div
-      class="flex cursor-pointer items-center space-x-15"
-      @click="$emit('onSaveDraft')"
-    >
-      <img
-        src=" ~/assets/img/note-create/create_note_save_tmp.png"
-        class="h-26 w-26"
-        alt=""
-        srcset=""
-      />
-      <span>保存草稿</span>
-    </div>
-  </div>
-</template>
-
-<script setup>
-defineEmits(['onInsertTitle', 'onInsertContent', 'onInsertImg', 'onSaveDraft'])
-</script>
-
-<style lang="scss" scoped></style>

+ 0 - 153
src/components/CreateNote/PreviewModal.vue

@@ -1,153 +0,0 @@
-<template>
-  <div>
-    <el-dialog
-      v-model="visible"
-      title="游记预览"
-      width="1000"
-      :z-index="9999"
-      destroy-on-close
-    >
-      <div class="max-h-[600px] overflow-auto">
-        <img
-          v-if="data.travelNotesBanner"
-          :src="data.travelNotesBanner"
-          class="h-auto w-full"
-        />
-        <img
-          v-else
-          src="~/assets/img/note-create/note_create_banner_bg.png"
-          class="h-auto w-full"
-        />
-        <div
-          class="mx-auto flex flex-1 items-center px-30 py-15 shadow-[0_15px_30px_0px_rgba(102,102,102,0.2)]"
-        >
-          <img
-            src="~/assets/img/travel_notes_detail/travel_note_icon.jpg"
-            class="h-50 w-50 shrink-0 object-cover"
-            alt=""
-            srcset=""
-          />
-          <div class="ml-10 flex-1">
-            <div class="text-3xl font-bold text-black-3">
-              {{ data.title ?? '游记标题' }}
-            </div>
-          </div>
-        </div>
-        <!-- <div
-          v-if="baseInfos.length"
-          class="mt-20 grid grid-cols-4 gap-y-15 rounded-xl border border-dashed border-black-9 px-15 py-15"
-        >
-          <div
-            v-for="item in baseInfos"
-            class="flex items-center space-x-2 text-base"
-          >
-            <img :src="item.icon" class="h-44 w-44 shrink-0" alt="" />
-            <span class="flex w-0 flex-1" :style="{ color: item.color }">
-              <span class="shrink-0 font-semibold">{{ item.lable }}/</span>
-              <span class="flex-1 truncate">{{ item.value }}</span>
-            </span>
-          </div>
-        </div> -->
-        <div class="mt-20">
-          <template v-for="item in data.travelNotesContent">
-            <div v-if="item.type === 'sectionTitle'">
-              <div class="py-10 text-3xl text-black-3">
-                {{ item.content }}
-              </div>
-            </div>
-            <div v-if="item.type === 'sectionContent'">
-              <div class="py-10 text-base leading-[28px] text-black-6">
-                {{ item.content }}
-              </div>
-            </div>
-            <div v-if="item.type === 'image'">
-              <el-image class="h-auto w-full py-10" :src="item.content" />
-            </div>
-          </template>
-        </div>
-      </div>
-      <template #footer>
-        <div class="flex items-center justify-center">
-          <el-button type="primary" @click="visible = false"> 确定 </el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-import travel_notes_detail_startdate from '~/assets/img/travel_notes_detail/travel_notes_detail_startdate.png'
-import travel_notes_detail_days from '~/assets/img/travel_notes_detail/travel_notes_detail_days.png'
-import travel_notes_detail_relation from '~/assets/img/travel_notes_detail/travel_notes_detail_relation.png'
-import travel_notes_detail_fee from '~/assets/img/travel_notes_detail/travel_notes_detail_fee.png'
-import travel_notes_detail_star from '~/assets/img/travel_notes_detail/travel_notes_detail_star.png'
-import travel_notes_detail_traffic from '~/assets/img/travel_notes_detail/travel_notes_detail_traffic.png'
-import travel_notes_detail_endplace from '~/assets/img/travel_notes_detail/travel_notes_detail_endplace.png'
-
-const visible = defineModel('visible', false)
-
-const props = defineProps({
-  data: {
-    type: Object,
-    default: () => ({})
-  },
-  travelModeLabel: String,
-  endPlace: String
-})
-
-const baseInfos = computed(() => {
-  const tmpList = []
-  if (props.data?.departureTime)
-    tmpList.push({
-      lable: '出发时间',
-      value: props.data?.departureTime,
-      color: '#4B99EA',
-      icon: travel_notes_detail_startdate
-    })
-  if (props.data?.countTimes)
-    tmpList.push({
-      lable: '出发天数',
-      value: props.data?.countTimes,
-      color: '#4B99EA',
-      icon: travel_notes_detail_days
-    })
-  if (props.data?.role)
-    tmpList.push({
-      lable: '人物关系',
-      value: props.data?.role,
-      color: '#4B99EA',
-      icon: travel_notes_detail_relation
-    })
-  if (props.data?.averageCost)
-    tmpList.push({
-      lable: '人均费用',
-      value: props.data?.averageCost,
-      color: '#4B99EA',
-      icon: travel_notes_detail_fee
-    })
-  if (props.travelModeLabel)
-    tmpList.push({
-      lable: '出行方式',
-      value: props.travelModeLabel,
-      color: '#4B99EA',
-      icon: travel_notes_detail_traffic
-    })
-  if (props.endPlace)
-    tmpList.push({
-      lable: '目的地',
-      value: props.data.endPlace,
-      color: '#4B99EA',
-      icon: travel_notes_detail_endplace
-    })
-  if (props.data?.recommendationRate)
-    tmpList.push({
-      lable: '推荐指数',
-      value: props.data?.recommendationRate,
-      color: '#facc58',
-      icon: travel_notes_detail_star
-    })
-  return tmpList
-})
-</script>
-
-<style lang="scss" scoped></style>

+ 34 - 28
src/components/CreateNote/PublishResultModal.client.vue

@@ -1,33 +1,41 @@
 <template>
-  <div>
-    <el-dialog
-      style="border-radius: 20px"
-      v-model="visible"
-      width="550"
-      @closed="onDialogClosed"
-    >
-      <div
-        class="flex w-full flex-col items-center space-y-10 rounded-[20px] py-25"
-      >
+  <van-dialog
+    style="border-radius: 20px"
+    v-model:show="visible"
+    width="295"
+    show-confirm-cutton
+    confirmButtonText=" "
+    confirmButtonDisabled
+    :show-cancel-button="false"
+  >
+    <div class="relative flex w-full flex-col items-center space-y-10 rounded-[20px] py-25">
+      <img src="~/assets/img/note-create/note_create_success.png" class="w-110" />
+      <div class="text-3xl font-semibold text-primary">提交成功</div>
+      <div class="text-base text-black-6">
+        审核中,可在
+        <NuxtLink to="/profile/notes" class="text-primary underline">我的游记</NuxtLink>
+        中查看审核结果
+      </div>
+      <div class="absolute w-32 h-32 top-0 right-10">
         <img
-          src="~/assets/img/note-create/note_create_success.png"
-          class="w-210"
+          @click="visible = false"
+          class="w-full h-full"
+          src="../../assets/img/note-create/close.svg"
+          alt=""
         />
-        <div class="text-2xl font-semibold text-primary">提交成功</div>
-        <div class="text-base text-black-6">
-          审核中,可在
-          <NuxtLink to="/profile/notes" class="text-primary underline"
-            >我的游记</NuxtLink
-          >
-          中查看审核结果
-        </div>
-        <div class="flex items-center justify-center space-x-20 pt-20">
-          <el-button type="primary" @click="handleNextNote">继续发布</el-button>
-          <el-button type="primary" @click="handleCheck">去查看</el-button>
-        </div>
       </div>
-    </el-dialog>
-  </div>
+    </div>
+    <template #footer>
+      <div class="flex items-center justify-center space-x-20 pt-20 pb-30">
+        <van-button round plain color="#FF9300" type="primary" @click="handleCheck">
+          去查看
+        </van-button>
+        <van-button round type="primary" color="#FF9300" @click="handleNextNote">
+          继续发布
+        </van-button>
+      </div>
+    </template>
+  </van-dialog>
 </template>
 
 <script setup>
@@ -35,8 +43,6 @@ const visible = defineModel('visible', false)
 
 const emit = defineEmits(['submitOk'])
 
-function onDialogClosed() {}
-
 function handleNextNote() {
   visible.value = false
   navigateTo('/note-create', {

+ 122 - 59
src/components/CreateNote/UserInfoModal.client.vue

@@ -1,74 +1,121 @@
 <template>
   <div>
-    <el-dialog
+    <van-dialog
       style="--el-dialog-padding-primary: 0px; border-radius: 20px"
-      v-model="visible"
-      width="550"
+      v-model:show="visible"
+      width="295"
       destroy-on-close
       :z-index="99"
       :show-close="false"
       @closed="onDialogClosed"
     >
       <div
-        class="relative flex w-full flex-col items-center rounded-[20px] bg-gradient-to-b from-[#ffe6c0] to-[#fffffe] py-30"
+        class="flex w-full flex-col items-center rounded-[20px] bg-gradient-to-b from-[#ffe6c0] to-[#fffffe] py-30"
       >
         <img
           src="~/assets/img/note-create/userinfo_modal.jpg"
-          class="absolute right-40 top-0 w-120"
+          class="w-70 absolute right-13 top-5"
         />
-        <div class="text-2xl font-semibold text-black-3">
-          请完善您的个人信息
-        </div>
-        <div class="-ml-90 mt-20">
-          <el-form
+        <div class="text-xl font-semibold text-black-3">请完善您的个人信息</div>
+        <div class="mt-20 w-255 relative">
+          <van-form
             ref="formRef"
-            :model="formData"
-            :rules="formRules"
-            label-width="100px"
-            style="--el-form-label-font-size: 13px; margin-top: 5px"
+            style="--van-cell-background: transparent !important"
+            class="w-full"
+            @submit="handleSubmit"
           >
-            <el-form-item label="昵称:" prop="showName">
-              <el-input
+            <van-cell-group style="--van-cell-background: transparent !important; padding: 0">
+              <van-field
+                clearable
+                autocomplete="off"
+                :rules="formRules.showName"
                 v-model="formData.showName"
-                :maxlength="50"
-                style="width: 300px"
-              />
-            </el-form-item>
-            <el-form-item label="住址:" prop="address">
-              <el-input
+                label-width="40"
+                placeholder=""
+              >
+                <template #label>
+                  <div class="w-40 h-40 h-40 flex items-center">
+                    <span class="text-[#ee0a24] pr-2">*</span>
+                    姓名:
+                  </div>
+                </template>
+              </van-field>
+              <van-field
+                clearable
+                autocomplete="off"
+                :rules="formRules.address"
                 v-model="formData.address"
-                :maxlength="100"
-                style="width: 300px"
-              />
-            </el-form-item>
-            <el-form-item label="邮箱:" prop="email">
-              <el-input
+                label-width="40"
+                placeholder=""
+              >
+                <template #label>
+                  <div class="w-40 h-40 h-40 flex items-center">
+                    <span class="text-[#fff]/[0] pr-2">*</span>
+                    住址:
+                  </div>
+                </template>
+              </van-field>
+              <van-field
+                clearable
+                autocomplete="off"
+                :rules="formRules.email"
                 v-model="formData.email"
-                :maxlength="100"
-                style="width: 300px"
-              />
-            </el-form-item>
-            <el-form-item label="职业:" prop="job">
-              <el-input
+                label-width="40"
+                placeholder=""
+              >
+                <template #label>
+                  <div class="w-40 h-40 h-40 flex items-center">
+                    <span class="text-[#ee0a24] pr-2">*</span>
+                    邮箱:
+                  </div>
+                </template>
+              </van-field>
+              <van-field
+                clearable
+                autocomplete="off"
+                :rules="formRules.job"
                 v-model="formData.job"
-                :maxlength="100"
-                style="width: 300px"
-              />
-            </el-form-item>
-          </el-form>
-        </div>
-        <div
-          @click="handleSubmit"
-          class="mt-20 flex h-40 w-110 cursor-pointer items-center justify-center rounded-full bg-primary text-xl font-semibold text-white hover:opacity-80"
-        >
-          提交
+                label-width="40"
+                placeholder=""
+              >
+                <template #label>
+                  <div class="w-40 h-40 h-40 flex items-center">
+                    <span class="text-[#fff]/[0] pr-2">*</span>
+                    职业:
+                  </div>
+                </template>
+              </van-field>
+            </van-cell-group>
+          </van-form>
         </div>
       </div>
-    </el-dialog>
+      <template #footer>
+        <div class="flex justify-around mb-20">
+          <button
+            native-type="submit"
+            @click="handleSubmit"
+            class="flex h-40 w-110 cursor-pointer items-center justify-center rounded-full bg-primary text-xl font-semibold text-white hover:opacity-80"
+          >
+            提交
+          </button>
+          <button
+            native-type="submit"
+            @click="visible = false"
+            class="flex h-40 w-110 cursor-pointer items-center justify-center rounded-full bg-primary text-xl font-semibold text-white hover:opacity-80"
+          >
+            取消
+          </button>
+        </div>
+      </template>
+      <!-- <div class="border fixed -bottom-20 left-1/2 z-100">
+        <img class="w-40 h-40" :src="close_circle" alt="" />
+      </div> -->
+    </van-dialog>
   </div>
 </template>
 
 <script setup>
+import close_circle from '~/assets/img/note-create/close-circle.svg'
 const visible = defineModel('visible', false)
 
 const emit = defineEmits(['submitOk'])
@@ -118,19 +165,35 @@ const formRef = ref(null)
 async function handleSubmit() {
   if (!formRef.value) return
 
-  await formRef.value.validate(async (valid) => {
-    if (valid) {
-      try {
-        await request('/website/tourism/publishTravelNotes/savePerfect', {
-          method: 'post',
-          body: formData
-        })
-        visible.value = false
-        emit('submitOk')
-      } catch (error) {}
-    }
-  })
+  if (formData.showName && formData.email) {
+    try {
+      await request('/website/tourism/publishTravelNotes/savePerfect', {
+        method: 'post',
+        body: formData
+      })
+      visible.value = false
+      emit('submitOk')
+    } catch (error) {}
+  }
 }
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+::v-deep .van-cell {
+  background: transparent !important;
+  padding: 0px 0px 10px 0px;
+}
+
+::v-deep .van-cell-group {
+  background: transparent !important;
+}
+
+::v-deep .van-field__body {
+  border: 1px solid #fd9a00;
+  width: 100%;
+  height: 40px;
+  border-radius: 8px;
+  background-color: white;
+  padding-left: 10px;
+}
+</style>

+ 6 - 11
src/components/Profile/Notes/Auditing/Item.vue

@@ -1,6 +1,6 @@
 <template>
   <van-swipe-cell>
-    <div class="group flex space-x-10 p-10 transition-all rounded-xl bg-[#FFF]">
+    <div class="relative group flex space-x-10 p-10 transition-all rounded-xl bg-[#FFF]">
       <img
         :src="formatImgSrc(data?.tourismUrlsAfterConvert) || noteDraftCoverBg"
         class="aspect-[4/3] h-109 shrink-0 object-cover"
@@ -15,6 +15,10 @@
             {{ $dayjs(data?.updateTime).format('YYYY/MM/DD') }}
           </div>
         </div>
+
+        <div class="absolute top-1/2 right-0 -translate-y-1/2 z-1 w-11 h-10">
+          <img class="w-full h-full" :src="left_arrow" alt="" />
+        </div>
         <!-- <div @click="$emit('onRevoke')" class="flex mb-10 items-center space-x-5">
           <div class="flex cursor-pointer items-center p-5 text-primary">
             <span class="w-15 h-15">
@@ -31,14 +35,6 @@
     </div>
 
     <template #right>
-      <!-- <div> -->
-      <!-- <span class="w-15 h-15">
-          <img
-            class="w-full h-full shrink-0 object-cover"
-            src="~/assets/img/note-create/cancel.svg"
-            alt=""
-          />
-        </span> -->
       <van-button
         square
         text="撤销审核"
@@ -46,14 +42,13 @@
         type="warning"
         class="delete-button"
       />
-      <!-- </div> -->
     </template>
   </van-swipe-cell>
 </template>
 
 <script setup>
 import noteDraftCoverBg from '~/assets/img/note-create/note_draft_cover_bg.jpg'
-
+import left_arrow from '~/assets/img/note-create/left.svg'
 const props = defineProps({
   data: {
     type: Object,

+ 30 - 25
src/components/Profile/Notes/Auditing/index.vue

@@ -1,28 +1,26 @@
 <template>
   <div class="w-full min-h-400 pb-10">
-    <!-- v-loading="loading" -->
-    <!-- v-if="!loading && !draftList.length" -->
-    <!-- <ProfileNotesEmpty v-if="!draftList.length" /> -->
-    <!-- v-else-if="draftList.length" -->
-    <!-- <div class="flex flex-col divide-y"> -->
-    <div class="flex flex-col divide-y">
-      <ProfileNotesAuditingItem />
-      <!-- v-for="item in draftList"
+    <ProfileNotesEmpty v-if="!loading && !draftList.length" />
+
+    <div v-else-if="draftList.length" class="flex flex-col divide-y">
+      <ProfileNotesAuditingItem
+        v-for="item in draftList"
         :key="item.id"
         :data="item"
-        @on-revoke="handleRevoke(item)" -->
+        @on-revoke="handleRevoke(item)"
+      />
     </div>
   </div>
 </template>
 
 <script setup>
-// const { loading, setLoading } = useLoading()
-// loading.value = true
+const { loading, setLoading } = useLoading()
+loading.value = true
 
 const draftList = ref([])
 
 async function getNotesList() {
-  // setLoading(true)
+  setLoading(true)
   try {
     const { data } = await request('/website/tourism/publishTravelNotes/getDraftList', {
       query: {
@@ -33,27 +31,34 @@ async function getNotesList() {
     })
     draftList.value = data.dataList
   } finally {
-    // setLoading(false)
+    setLoading(false)
   }
 }
 
 async function handleRevoke(item) {
-  ElMessageBox.confirm('确定撤销审核吗?', '提示', {}).then(async () => {
-    try {
-      await request(`/website/tourism/publishTravelNotes/withdraw`, {
-        method: 'post',
-        body: {
-          writeId: item.id
-        }
-      })
-      ElMessage.success('撤销成功')
-      getNotesList()
-    } catch (error) {}
+  showConfirmDialog({
+    title: '提示',
+    message: '确定撤销审核吗?'
   })
+    .then(async () => {
+      try {
+        await request(`/website/tourism/publishTravelNotes/withdraw`, {
+          method: 'post',
+          body: {
+            writeId: item.id
+          }
+        })
+        showSuccessToast('撤销成功')
+        getNotesList()
+      } catch (error) {}
+    })
+    .catch(() => {
+      // on cancel
+    })
 }
 
 onMounted(() => {
-  // getNotesList()
+  getNotesList()
 })
 </script>
 

+ 13 - 17
src/components/Profile/Notes/Draft/Item.vue

@@ -1,6 +1,6 @@
 <template>
   <van-swipe-cell>
-    <div class="group flex space-x-10 p-10 transition-all mb-10 bg-[#fff] rounded-xl">
+    <div class="relative group flex space-x-10 p-10 transition-all mb-10 bg-[#fff] rounded-xl">
       <img
         :src="formatImgSrc(data?.tourismUrlsAfterConvert) || noteDraftCoverBg"
         class="aspect-[4/3] h-109 shrink-0 rounded-xl object-cover"
@@ -15,32 +15,28 @@
             {{ $dayjs(data?.updateTime).format('YYYY-MM-DD') }}
           </div>
         </div>
-        <!-- <div class="flex items-center space-x-10">
-          <div
-            @click="handleWrite"
-            class="flex cursor-pointer items-center space-x-5 rounded border border-[#f0f2f5] px-10 py-5 text-primary hover:bg-primary hover:text-white"
-          >
-            <span class="iconfont icon-edit-square" style="font-size: 14px"></span>
-            <span class="text-sm">继续写</span>
-          </div>
-          <div
-            @click="$emit('onDelete')"
-            class="iconfont icon-delete hidden cursor-pointer text-black-6 group-hover:block"
-            style="font-size: 18px"
-          ></div>
-        </div> -->
+      </div>
+
+      <div class="absolute top-1/2 right-0 -translate-y-1/2 z-1 w-11 h-10">
+        <img class="w-full h-full" :src="left_arrow" alt="" />
       </div>
     </div>
     <template #right>
       <van-button square text="继续写" @click="handleWrite" type="warning" class="delete-button" />
-      <van-button square text="删除" @click="handleWrite" type="danger" class="delete-button" />
+      <van-button
+        square
+        text="删除"
+        @click="$emit('onDelete')"
+        type="danger"
+        class="delete-button"
+      />
     </template>
   </van-swipe-cell>
 </template>
 
 <script setup>
 import noteDraftCoverBg from '~/assets/img/note-create/note_draft_cover_bg.jpg'
-
+import left_arrow from '~/assets/img/note-create/left.svg'
 const props = defineProps({
   data: {
     type: Object,

+ 23 - 17
src/components/Profile/Notes/Draft/index.vue

@@ -1,9 +1,8 @@
 <template>
   <div class="min-h-400 pb-10">
-    <!-- v-loading="loading" -->
     <ProfileNotesEmpty v-if="!loading && !draftList.length" />
-    <!--  v-else-if="draftList.length" -->
-    <div>
+
+    <div v-else-if="draftList.length">
       <div class="text-black-3 px-10">
         <p class="text-base font-bold">{{ draftList?.length }}篇草稿</p>
         <p class="text-sm text-[#FF2929]">
@@ -11,12 +10,12 @@
         </p>
       </div>
       <div class="grid grid-cols-1">
-        <ProfileNotesDraftItem 
+        <ProfileNotesDraftItem
           v-for="item in draftList"
           :key="item.id"
           :data="item"
-          @on-delete="handleDelete(item)"  />
-     
+          @on-delete="handleDelete(item)"
+        />
       </div>
     </div>
   </div>
@@ -45,18 +44,25 @@ async function getNotesList() {
 }
 
 async function handleDelete(item) {
-  ElMessageBox.confirm('确定删除吗?', '提示', {}).then(async () => {
-    try {
-      await request(`/website/tourism/publishTravelNotes/removeByDraftId`, {
-        method: 'post',
-        body: {
-          writeId: item.id
-        }
-      })
-      ElMessage.success('删除成功')
-      // getNotesList()
-    } catch (error) {}
+  showConfirmDialog({
+    title: '提示',
+    message: '确定删除吗?'
   })
+    .then(async () => {
+      try {
+        await request(`/website/tourism/publishTravelNotes/removeByDraftId`, {
+          method: 'post',
+          body: {
+            writeId: item.id
+          }
+        })
+        showSuccessToast('删除成功')
+        getNotesList()
+      } catch (error) {}
+    })
+    .catch(() => {
+      // on cancel
+    })
 }
 
 onMounted(() => {

+ 13 - 5
src/components/Profile/Notes/Published/Item.vue

@@ -1,6 +1,6 @@
 <template>
   <van-swipe-cell>
-    <div class="group flex border-box p-10 transition-all mb-10 bg-[#fff] rounded-xl">
+    <div class="relative group flex border-box p-10 transition-all mb-10 bg-[#fff] rounded-xl">
       <div class="aspect-[4/3] h-109 border-box">
         <van-image
           width="100%"
@@ -27,12 +27,19 @@
             <span class="iconfont icon-eye-fill text-[#FD9A00]" style="font-size: 15px"></span>
             <div>{{ data?.pageViewCount ?? 0 }}</div>
           </div>
-          <div v-if="data?.endPlaceDictMap" class="flex items-center space-x-2">
+          <div class="pl-20 flex items-center space-x-2">
+            <van-icon @click="$emit('onShare')" name="share-o" color="#FD9A00" />
+          </div>
+          <!-- <div v-if="data?.endPlaceDictMap" class="flex items-center space-x-2">
             <span class="iconfont icon-location-fill text-black-9" style="font-size: 15px"></span>
             <div>{{ data?.endPlaceDictMap?.name }}</div>
-          </div>
+          </div> -->
         </div>
       </div>
+
+      <div class="absolute top-1/2 right-0 -translate-y-1/2 z-1 w-11 h-10">
+        <img class="w-full h-full" :src="left_arrow" alt="" />
+      </div>
     </div>
     <template #right>
       <van-button
@@ -48,7 +55,7 @@
 
 <script setup>
 import noteDraftCoverBg from '~/assets/img/note-create/note_draft_cover_bg.jpg'
-
+import left_arrow from '~/assets/img/note-create/left.svg'
 const props = defineProps({
   data: {
     type: Object,
@@ -56,7 +63,8 @@ const props = defineProps({
   }
 })
 
-defineEmits(['onNoteDown'])
+defineEmits(['onNoteDown', 'onShare'])
+//
 </script>
 
 <style lang="scss" scoped>

+ 110 - 35
src/components/Profile/Notes/Published/index.vue

@@ -1,42 +1,110 @@
 <template>
   <div class="w-full box-border min-h-400 pb-10 px-1">
     <ProfileNotesEmpty v-if="!noteList.length" />
+    <div v-else-if="noteList.length">
+      <ProfileNotesPublishedItem
+        v-for="item in noteList"
+        :key="item.id"
+        :data="item"
+        @on-note-down="handleNoteDown(item)"
+        @on-share="changeShowShare(item)"
+      />
+    </div>
 
-    <ProfileNotesPublishedItem
-      v-for="item in noteList"
-      :key="item.id"
-      :data="item"
-      @on-note-down="handleNoteDown(item)"
-    />
+    <van-share-sheet v-model:show="showShare" :options="options" @select="onSelectShare" />
   </div>
 </template>
 
 <script setup>
-const { loading, setLoading } = useLoading()
+// import wx from 'weixin-js-sdk'
+const showShare = ref(false)
+
+const options = [
+  { name: '微信', icon: 'wechat' },
+  { name: '微博', icon: 'weibo' },
+  { name: '复制链接', icon: 'link' },
+  { name: '分享海报', icon: 'poster' },
+  { name: '二维码', icon: 'qrcode' }
+]
+
+const yjId = ref(null)
+
+const changeShowShare = (parmas) => {
+  showShare.value = true
+  yjId.value = parmas.id
+}
+// 分享
+const onSelectShare = (option) => {
+  if (option.name === '微信') {
+    shareToWeChat('friend')
+  } else if (option.name === '朋友圈') {
+    shareToWeChat('timeline')
+  } else if (option.name === '复制链接') {
+    copyLink()
+  }
+  showShare.value = false
+}
+
+// 微信分享到好友
+const shareToWeChat = (type) => {
+  // const shareUrl = 'https://your-website.com/share-page' // 分享链接
+  // const title = '分享标题' // 分享标题
+  // const desc = '分享描述' // 分享描述
+  // const imgUrl = 'https://your-website.com/share-image.jpg' // 分享图片
+  // getWeChatConfig().then((configData) => {
+  //   wx.config({
+  //     debug: false, // 设置为 true 以便调试
+  //     appId: configData.appId,
+  //     timestamp: configData.timestamp,
+  //     nonceStr: configData.nonceStr,
+  //     signature: configData.signature,
+  //     jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'] // 配置微信 JS-SDK 的接口
+  //   })
+  //   // 通过微信 JS-SDK 实现分享功能(已配置过微信 JS-SDK)
+  //   if (typeof wx !== 'undefined') {
+  //     wx.ready(function () {
+  //       const shareData = {
+  //         title: title,
+  //         desc: desc,
+  //         link: shareUrl,
+  //         imgUrl: imgUrl
+  //       }
+  //       if (type === 'friend') {
+  //         wx.updateAppMessageShareData(shareData) // 分享给朋友
+  //       } else if (type === 'timeline') {
+  //         wx.updateTimelineShareData(shareData) // 分享到朋友圈
+  //       }
+  //     })
+  //   }
+  //   wx.error((error) => {
+  //     console.error('微信 JS-SDK 配置失败:', error)
+  //   })
+  // })
+}
+
+const copyLink = () => {
+  const url = `${import.meta.env.VITE_APP_BASE_URL}yj/${yjId.value}`
+  navigator.clipboard.writeText(url).then(() => {
+    showToast('链接已复制')
+  })
+}
+// const { loading, setLoading } = useLoading()
 
 const noteList = ref([])
 const pageNum = ref(1)
 
-function getList() {
+async function getList() {
   try {
-    // let {
-    //   data: { dataList }
-    // } = await request('/website/tourism/publishTravelNotes/getDraftList', {
-    //   query: {
-    //     pageNum: pageNum.value,
-    //     pageSize: 10,
-    //     type: 3
-    //   }
-    // })
-    let dataList = [
-      {
-        id: '1'
+    let {
+      data: { dataList }
+    } = await request('/website/tourism/publishTravelNotes/getDraftList', {
+      query: {
+        pageNum: pageNum.value,
+        pageSize: 10000,
+        type: 3
       }
-    ]
-    console.log(1231)
+    })
     if (Array.isArray(dataList) && dataList.length) {
-      console.log(1231444)
-
       noteList.value = dataList
       pageNum.value++
     }
@@ -45,18 +113,25 @@ function getList() {
 
 // 下架
 async function handleNoteDown(item) {
-  ElMessageBox.confirm('下架后游记将存入草稿,确定下架吗?', '提示', {}).then(async () => {
-    try {
-      await request(`/website/tourism/publishTravelNotes/delist`, {
-        method: 'post',
-        body: {
-          writeId: item.id
-        }
-      })
-      ElMessage.success('下架成功')
-      getList()
-    } catch (error) {}
+  showConfirmDialog({
+    title: '提示',
+    message: '下架后游记将存入草稿,确定下架吗?'
   })
+    .then(async () => {
+      try {
+        await request(`/website/tourism/publishTravelNotes/delist`, {
+          method: 'post',
+          body: {
+            writeId: item.id
+          }
+        })
+        showSuccessToast('下架成功')
+        getList()
+      } catch (error) {}
+    })
+    .catch(() => {
+      // on cancel
+    })
 }
 
 onMounted(() => {

+ 6 - 19
src/components/Profile/Notes/Rejected/Item.vue

@@ -1,6 +1,6 @@
 <template>
   <van-swipe-cell>
-    <div class="group flex space-x-10 p-10 transition-all mb-10 bg-[#fff] rounded-xl">
+    <div class="relative group flex space-x-10 p-10 transition-all mb-10 bg-[#fff] rounded-xl">
       <img
         :src="formatImgSrc(data?.tourismUrlsAfterConvert) || noteDraftCoverBg"
         class="aspect-[4/3] h-109 shrink-0 object-cover"
@@ -11,27 +11,14 @@
           <div class="mt-15 line-clamp-2 break-all text-xl font-semibold text-black-3">
             {{ data?.projectTitle || '未命名草稿' }}
           </div>
-          <!-- <div class="mt-10 text-sm text-black-9">
-            {{ $dayjs(data?.updateTime).format('YYYY-MM-DD') }}
-          </div> -->
+
           <div class="line-clamp-3 text-sm text-red-600">
             审核未通过:{{ data?.reason || '内容可能涉嫌色情淫秽传播,请修改内容' }}
           </div>
         </div>
-        <!-- <div class="flex items-center space-x-10">
-          <div
-            @click="handleWrite"
-            class="flex cursor-pointer items-center space-x-5 rounded border border-[#f0f2f5] px-10 py-5 text-primary hover:bg-primary hover:text-white"
-          >
-            <span class="iconfont icon-edit-square" style="font-size: 14px"></span>
-            <span class="text-sm">编辑</span>
-          </div>
-          <div
-            @click="$emit('onDelete')"
-            class="iconfont icon-delete hidden cursor-pointer text-black-6 group-hover:block"
-            style="font-size: 18px"
-          ></div>
-        </div> -->
+      </div>
+      <div class="absolute top-1/2 right-0 -translate-y-1/2 z-1 w-11 h-10">
+        <img class="w-full h-full" :src="left_arrow" alt="" />
       </div>
     </div>
     <template #right>
@@ -49,7 +36,7 @@
 
 <script setup>
 import noteDraftCoverBg from '~/assets/img/note-create/note_draft_cover_bg.jpg'
-
+import left_arrow from '~/assets/img/note-create/left.svg'
 const props = defineProps({
   data: {
     type: Object,

+ 26 - 22
src/components/Profile/Notes/Rejected/index.vue

@@ -1,18 +1,15 @@
 <template>
   <div class="min-h-400 pb-10">
-    <!--  v-loading="loading" -->
-    <!-- <ProfileNotesEmpty /> -->
-    <!-- v-if="!loading && !draftList.length" -->
-    <!-- v-else-if="draftList.length" -->
-    <!-- <div> -->
-    <div class="grid grid-cols-1">
-      <ProfileNotesRejectedItem />
-      <!-- v-for="item in draftList"
+    <ProfileNotesEmpty v-if="!loading && !draftList.length" />
+
+    <div v-else-if="draftList.length" class="grid grid-cols-1">
+      <ProfileNotesRejectedItem
+        v-for="item in draftList"
         :key="item.id"
         :data="item"
-        @on-delete="handleDelete(item)" -->
+        @on-delete="handleDelete(item)"
+      />
     </div>
-    <!-- </div> -->
   </div>
 </template>
 
@@ -39,22 +36,29 @@ async function getNotesList() {
 }
 
 async function handleDelete(item) {
-  ElMessageBox.confirm('确定删除吗?', '提示', {}).then(async () => {
-    try {
-      await request(`/website/tourism/publishTravelNotes/removeByDraftId`, {
-        method: 'post',
-        body: {
-          writeId: item.id
-        }
-      })
-      ElMessage.success('删除成功')
-      getNotesList()
-    } catch (error) {}
+  showConfirmDialog({
+    title: '提示',
+    message: '确定删除吗?'
   })
+    .then(async () => {
+      try {
+        await request(`/website/tourism/publishTravelNotes/removeByDraftId`, {
+          method: 'post',
+          body: {
+            writeId: item.id
+          }
+        })
+        showSuccessToast('删除成功')
+        getNotesList()
+      } catch (error) {}
+    })
+    .catch(() => {
+      // on cancel
+    })
 }
 
 onMounted(() => {
-  // getNotesList()
+  getNotesList()
 })
 </script>
 

+ 24 - 0
src/middleware/create-note.global.js

@@ -0,0 +1,24 @@
+export default defineNuxtRouteMiddleware(async (to, from) => {
+  if (import.meta.server) return
+
+  const { token } = useAuthStore()
+  if (!token) return
+
+  const { query, name } = to
+  if (name === 'note-create' && !query.id) {
+    try {
+      const res = await request('/website/tourism/publishTravelNotes/getDraftId')
+      const id = res.data
+      return navigateTo({
+        path: '/note-create',
+        query: {
+          id
+        },
+        replace: true
+      })
+    } catch (error) {
+      return
+    }
+  }
+  return
+})

+ 515 - 315
src/pages/note-create/index.client.vue

@@ -1,280 +1,413 @@
 <template>
-  <div v-if="!loading" class="box-border pb-80">
-    <CreateNoteHeaderBanner v-model:bannerUrl="noteJson.travelNotesBanner" />
-
-    <CreateNoteForm
-      v-model:departureTime="noteJson.departureTime"
-      v-model:countTimes="noteJson.countTimes"
-      v-model:endPlace="noteJson.endPlace"
-      v-model:role="noteJson.role"
-      v-model:travelMode="noteJson.travelMode"
-      v-model:averageCost="noteJson.averageCost"
-      v-model:recommendationRate="noteJson.recommendationRate"
-      v-model:travelNumber="noteJson.travelNumber"
-    />
-
-    <div class="flex items-center pl-16 pt-16 w-full h-40">
-      <div class="w-2 h-14 bg-[#FF9300] mr-16"></div>
-      <h1 class="text-sm font-bold">编辑游记文章</h1>
-    </div>
-    <div class="mb-12">
-      <van-cell-group class="border" inset>
-        <van-field
-          v-model="noteJson.projectTitle"
-          rows="1"
-          autosize
-          type="textarea"
-          placeholder="从这里开始游记大标题..."
-          maxlength="50"
-          show-word-limit
-        ></van-field>
-      </van-cell-group>
-    </div>
-
-    <template v-for="(item, index) in noteJson.travelNotesContent" :key="item.tmpId">
-      <div v-if="item.type === defaultSectionTitle.type" class="h-50 mb-12 relative box-border">
-        <van-cell-group class="border h-full" inset>
+  <div v-if="!loading" class="box-border pb-85">
+    <div v-if="!previewOptions.show">
+      <CreateNoteHeaderBanner v-model:bannerUrl="noteJson.travelNotesBanner" />
+
+      <CreateNoteForm
+        v-model:departureTime="noteJson.departureTime"
+        v-model:countTimes="noteJson.countTimes"
+        v-model:endPlace="noteJson.endPlace"
+        v-model:role="noteJson.role"
+        v-model:travelMode="noteJson.travelMode"
+        v-model:averageCost="noteJson.averageCost"
+        v-model:recommendationRate="noteJson.recommendationRate"
+        v-model:travelNumber="noteJson.travelNumber"
+      />
+
+      <div class="flex items-center pl-16 pt-16 w-full h-40">
+        <div class="w-2 h-14 bg-[#FF9300] mr-16"></div>
+        <h1 class="text-sm font-bold">编辑游记文章</h1>
+      </div>
+      <div class="mb-12">
+        <van-cell-group class="border" inset>
           <van-field
-            v-model="item.content"
+            v-model="noteJson.projectTitle"
             rows="1"
             autosize
-            class="text-base"
+            clearable
             type="textarea"
-            trigger="onChange"
-            validate-trigger="onChange"
-            placeholder="请输入段落标题..."
-            @click-input="handleInsertOrEditTitleIndex(index)"
-            @update:model-value="handleInsertOrEditTitle"
-          >
-            <template #extra>
-              <div class="w-16 h-16 z-0">
-                <img
-                  @click="handleDeleteTitle(index)"
-                  class="w-full h-full"
-                  src="~/assets/img/note-create/delete.svg"
-                  alt=""
-                />
-              </div>
-            </template>
-          </van-field>
+            @update:model-value="handleInsertOrEditProjectTitle"
+            placeholder="从这里开始游记大标题..."
+            maxlength="50"
+            show-word-limit
+          ></van-field>
         </van-cell-group>
       </div>
+      <VueDraggable v-model="noteJson.travelNotesContent" :options="dragOptions">
+        <template v-for="(item, index) in noteJson.travelNotesContent" :key="item.tmpId">
+          <div v-if="item.type === defaultSectionTitle.type" class="h-50 mb-12 relative box-border">
+            <van-cell-group class="border h-full" inset>
+              <van-field
+                v-model="item.content"
+                rows="1"
+                autosize
+                class="text-base"
+                type="textarea"
+                trigger="onChange"
+                validate-trigger="onChange"
+                placeholder="请输入段落标题..."
+                @click-input="handleInsertOrEditTitleIndex(index)"
+                @update:model-value="handleInsertOrEditTitle"
+              >
+                <template #extra>
+                  <div class="w-16 h-16 z-0">
+                    <img
+                      @click="handleDeleteTitle(index)"
+                      class="w-full h-full"
+                      src="~/assets/img/note-create/delete.svg"
+                      alt=""
+                    />
+                  </div>
+                </template>
+              </van-field>
+            </van-cell-group>
+          </div>
 
-      <div
-        v-else-if="item.type === defaultSectionContent.type"
-        class="h-100 mb-12 relative box-border"
-      >
-        <van-cell-group class="border h-full" inset>
-          <van-field
-            v-model="item.content"
-            rows="1"
-            autosize
-            class="text-base border"
-            type="textarea"
-            trigger="onChange"
-            validate-trigger="onChange"
-            placeholder="请在这里输入游记正文..."
-            @click-input="handleInsertOrEditTitleIndex(index)"
-            @update:model-value="handleInsertOrEditTitle"
+          <div
+            v-else-if="item.type === defaultSectionContent.type"
+            class="h-100 mb-12 relative box-border"
           >
-            <template #extra>
-              <div class="w-16 h-16 z-0">
-                <img
-                  @click="handleDeleteTitle(index)"
-                  class="w-full h-full"
-                  src="../../assets/img/note-create/delete.svg"
-                  alt=""
-                />
+            <!-- focus:outline-none focus:caret-[#FF9300] active:border-[#FF9300]  focus:border-[#FF9300]-->
+            <van-cell-group class="border h-full" inset>
+              <van-field
+                autofocus
+                v-model="item.content"
+                rows="1"
+                autosize
+                class="text-base"
+                type="textarea"
+                trigger="onChange"
+                validate-trigger="onChange"
+                placeholder="请在这里输入游记正文..."
+                @click-input="handleInsertOrEditTitleIndex(index)"
+                @update:model-value="handleInsertOrEditTitle"
+              >
+                <template #extra>
+                  <div class="w-16 h-16 z-0">
+                    <img
+                      @click="handleDeleteTitle(index)"
+                      class="w-full h-full"
+                      src="../../assets/img/note-create/delete.svg"
+                      alt=""
+                    />
+                  </div>
+                </template>
+              </van-field>
+            </van-cell-group>
+          </div>
+          <!-- 有图片的样式 -->
+          <div v-else-if="item.type === defaultSectionImage.type" class="relative box-border mb-12">
+            <template v-if="item.content">
+              <div>
+                <van-cell-group class="border h-full" inset>
+                  <div v-if="item.content">
+                    <img :src="item.content" alt="Uploaded Image" />
+                  </div>
+                </van-cell-group>
+                <div
+                  class="w-110 rounded-xl box-border px-10 h-20 flex justify-between items-center h-16 absolute top-12 right-26 z-100 bg-[#fff]/[0.5]"
+                >
+                  <span
+                    @click="handleSaveCover(item)"
+                    class="text-sm pr-7 border-r-[1px] border-[#F3F3F3] text-black-3"
+                  >
+                    设为封面图
+                  </span>
+                  <div class="w-16 h-16 inline-block">
+                    <img
+                      @click="handleDeleteImage(index)"
+                      class="w-full h-full"
+                      src="../../assets/img/note-create/delete.svg"
+                      alt=""
+                    />
+                  </div>
+                </div>
               </div>
             </template>
-          </van-field>
-        </van-cell-group>
-      </div>
-
-      <div
-        v-else-if="item.type === defaultSectionImage.type"
-        class="relative box-border h-203 mb-12"
-      >
-        <van-cell-group class="border h-full" inset>
-          <!-- <van-field>
-            <template #input>
-              <van-uploader v-model="value">
-                <template #default>
+            <!-- 没有图片的样式 -->
+            <template v-else>
+              <div class="h-200">
+                <van-cell-group class="border h-full" inset>
+                  <!-- <div v-if="itemsImage">
+                    <img ref="cropperRef" class="w-full h-full" :src="itemsImage" alt="" />
+                  </div> v-else -->
                   <div class="w-full h-full flex justify-center items-center">
                     <button
+                      @click="handleSelectImage(index)"
                       class="box-border active:bg-[#000]/[0.1] text-base flex justify-center items-center px-28 py-10 rounded-full border"
                     >
                       <div class="h-16 w-16 mr-8">
-                        <img
-                          class="w-full h-full"
-                          src="../../assets/img/note-create/img.svg"
-                          alt=""
-                        />
+                        <img class="w-full h-full" src="~/assets/img/note-create/img.svg" alt="" />
                       </div>
 
                       插入图片
                     </button>
                   </div>
-                </template>
-              </van-uploader>
-            </template>
-          </van-field> -->
-
-          <div class="w-full h-full flex justify-center items-center">
-            <van-uploader v-model="fileList" :after-read="afterRead" :multiple="false" >
-              <button
-                @click="handleInsertOrEditTitleIndex(index)"
-                class="box-border active:bg-[#000]/[0.1] text-base flex justify-center items-center px-28 py-10 rounded-full border"
-              >
-                <div class="h-16 w-16 mr-8">
-                  <img class="w-full h-full" src="~/assets/img/note-create/img.svg" alt="" />
+                </van-cell-group>
+
+                <div
+                  class="w-86 flex justify-between items-center h-16 absolute top-10 right-26 z-100"
+                >
+                  <span @click="handleSaveCover(item)" class="text-sm text-black-3">
+                    设为封面图
+                  </span>
+                  <div class="w-16 h-16 inline-block">
+                    <img
+                      @click="handleDeleteImage(index)"
+                      class="w-full h-full"
+                      src="~/assets/img/note-create/delete.svg"
+                      alt=""
+                    />
+                  </div>
                 </div>
-
-                插入图片
-              </button>
-            </van-uploader>
-            
+              </div>
+            </template>
           </div>
-        </van-cell-group>
-        <div v-if="item.content ">
-              <img :src="item.content" alt="Uploaded Image" />
-        </div>
+        </template>
+      </VueDraggable>
+
+      <div class="px-16 flex justify-between">
+        <button
+          @click="handleInsertOrEditTitleOk(defaultSectionTitle)"
+          class="w-110 active:bg-[#000]/[0.1] h-44 box-border text-base text-black-3 flex justify-center rounded-md items-center bg-[#F3F3F3]"
+        >
+          <van-icon name="plus" class="mr-5" color="#FF9300" />
+          插入小标题
+        </button>
+        <button
+          @click="handleInsertOrEditTitleOk(defaultSectionContent)"
+          class="w-110 h-44 active:bg-[#000]/[0.1] box-border text-base text-black-3 flex justify-center rounded-md items-center bg-[#F3F3F3]"
+        >
+          <van-icon name="plus" class="mr-5" color="#FF9300" />
+          插入内容
+        </button>
+        <button
+          @click="handleInsertOrEditTitleOk(defaultSectionImage)"
+          class="w-110 active:bg-[#000]/[0.1] h-44 box-border text-base text-black-3 flex justify-center rounded-md items-center bg-[#F3F3F3]"
+        >
+          <van-icon name="plus" class="mr-5" color="#FF9300" />
+          插入图片
+        </button>
+      </div>
 
-        <div class="w-86 flex justify-between items-center h-16 absolute top-[10px] right-26 z-1">
-          <span @click="handleSaveCover(item)" class="text-sm text-black-3">设为封面图</span>
-          <div class="w-16 h-16 inline-block">
-            <img
-              @click="handleDeleteImage(index)"
-              class="w-full h-full"
-              src="../../assets/img/note-create/delete.svg"
-              alt=""
-            />
+      <div
+        class="fixed box-border p-16 shadow-[0_-4px_4px_0px_rgba(0,0,0,0.1)] bottom-0 left-0 w-full h-80 flex justify-between bg-white items-center"
+      >
+        <div class="flex justify-start items-center h-50">
+          <div class="w-50 text-center" @click="showDialog(draftDialogContent)">
+            <van-icon :name="draft" size="20px" />
+            <p class="text-sm text-black-3">草稿</p>
+          </div>
+          <div class="w-50 text-center" @click="handlePreview">
+            <van-icon name="eye-o" size="24px" />
+            <p class="text-sm text-black-3">预览</p>
           </div>
         </div>
-      </div>
-    </template>
 
-    <div class="px-16 flex justify-between">
-      <button
-        @click="handleInsertOrEditTitleOk(defaultSectionTitle)"
-        class="w-110 active:bg-[#000]/[0.1] h-44 box-border text-base text-black-3 flex justify-center rounded-md items-center bg-[#F3F3F3]"
-      >
-        <van-icon name="plus" class="mr-5" color="#FF9300" />
-        插入小标题
-      </button>
-      <button
-        @click="handleInsertOrEditTitleOk(defaultSectionContent)"
-        class="w-110 h-44 active:bg-[#000]/[0.1] box-border text-base text-black-3 flex justify-center rounded-md items-center bg-[#F3F3F3]"
-      >
-        <van-icon name="plus" class="mr-5" color="#FF9300" />
-        插入内容
-      </button>
-      <!-- @click="handleInsertOrEditTitleOk(defaultSectionImage)" -->
-      <button
-        @click="handleInsertOrEditTitleOk(defaultSectionImage)"
-        class="w-110 active:bg-[#000]/[0.1] h-44 box-border text-base text-black-3 flex justify-center rounded-md items-center bg-[#F3F3F3]"
-      >
-        <van-icon name="plus" class="mr-5" color="#FF9300" />
-        插入图片
-      </button>
+        <van-button
+          class="w-full h-full flex items-center"
+          size="large"
+          type="primary"
+          round
+          color="#FD9A00"
+          block
+          :loading="publishLoading"
+          @click="requestPublish"
+        >
+          <template #icon>
+            <van-image width="22" height="22" :src="upload" />
+          </template>
+          发布
+        </van-button>
+      </div>
     </div>
 
-    <div
-      class="fixed box-border p-16 shadow-[0_-4px_4px_0px_rgba(0,0,0,0.1)] bottom-0 left-0 w-full h-80 flex justify-between bg-white items-center"
-    >
-      <div class="flex justify-start items-center h-50">
-        <div class="w-50 text-center"  @click="handleSaveDraft">
-          <van-icon name="notes-o" size="24px" />
-          <p class="text-sm text-black-3">草稿</p>
+    <div v-else class="w-full h-full">
+      <div class="p-10 pb-80">
+        <img
+          v-if="defaultNoteJson?.travelNotesBanner"
+          :src="defaultNoteJson?.travelNotesBanner"
+          class="aspect-[316/204] w-full object-cover rounded-lg"
+        />
+        <img
+          v-else
+          src="~/assets/img/note-create/note_create_banner_bg.png"
+          class="aspect-[316/204] w-full object-cover rounded-lg"
+        />
+
+        <div class="flex mt-10">
+          <img src="~/assets/img/article_title.png" class="w-[32px] h-[32px] shrink-0" alt="" />
+
+          <div
+            class="w-321 line-clamp-2 overflow-hidden truncate max-w-xs ml-10 text-xl whitespace-normal font-bold text-black-3"
+          >
+            {{ defaultNoteJson?.projectTitle ?? '游记标题' }}
+          </div>
         </div>
-        <div class="w-50 text-center">
-          <van-icon name="eye-o" size="24px" />
-          <p class="text-sm text-black-3">预览</p>
+        <div class="flex justify-end text-[#999] text-[12px]">
+          {{ defaultNoteJson.createTime }}
+        </div>
+        <div
+          class="relative border-2 border-dashed border-[#E3E3E3] pt-15 pb-15 mt-12 rounded-lg pl-20 text-[#4B99EA] text-[12px]"
+          :style="{ background: `url(${dashBorder2})`, backgroundSize: '100% 100%' }"
+        >
+          <div class="">
+            <van-row>
+              <van-col span="12">
+                <div class="w-[90%] flex">
+                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/date.png" alt="" />
+                  <div class="font-bold leading-[20px]">出发时间/</div>
+                  <div class="leading-[20px] flex-1">
+                    {{ defaultNoteJson?.departureTime || '' }}
+                  </div>
+                </div>
+              </van-col>
+              <van-col span="12">
+                <div class="w-[90%] flex">
+                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/time.png" alt="" />
+                  <div class="font-bold leading-[20px]">出发天数/</div>
+                  <div class="leading-[20px] flex-1">{{ defaultNoteJson?.countTimes || '' }}</div>
+                </div>
+              </van-col>
+            </van-row>
+          </div>
+          <div class="mt-24">
+            <van-row>
+              <van-col span="12">
+                <div class="w-[90%] flex">
+                  <img
+                    class="w-[20px] h-[20px] mr-5"
+                    src="~/assets/img/yj/relationship.png"
+                    alt=""
+                  />
+                  <div class="font-bold leading-[20px]">人物关系/</div>
+                  <div class="leading-[20px] flex-1">{{ defaultNoteJson?.role || '' }}</div>
+                </div>
+              </van-col>
+              <van-col span="12">
+                <div class="w-[90%] flex">
+                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/money.png" alt="" />
+                  <div class="font-bold leading-[20px]">人均费用/</div>
+                  <div class="leading-[20px] flex-1">{{ defaultNoteJson?.averageCost || '' }}</div>
+                </div>
+              </van-col>
+            </van-row>
+          </div>
+          <div class="mt-24">
+            <van-row>
+              <van-col span="12">
+                <div class="w-[90%] flex">
+                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/plan.png" alt="" />
+                  <div class="font-bold leading-[20px]">出行方式/</div>
+                  <div class="leading-[20px] flex-1">
+                    {{ defaultNoteJson?.travelModeDictMap?.name || '' }}
+                  </div>
+                </div>
+              </van-col>
+              <van-col span="12">
+                <div class="w-[90%] flex">
+                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/target.png" alt="" />
+                  <div class="font-bold leading-[20px]">目的地/</div>
+                  <div class="leading-[20px] flex-1">
+                    {{ defaultNoteJson?.endPlaceDictMap?.name || '' }}
+                  </div>
+                </div>
+              </van-col>
+            </van-row>
+          </div>
+          <div class="mt-24">
+            <van-row>
+              <van-col span="12">
+                <div class="w-[90%] flex">
+                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/person.png" alt="" />
+                  <div class="font-bold leading-[20px]">游玩人数/</div>
+                  <div class="leading-[20px] flex-1">{{ defaultNoteJson?.travelNumber || '' }}</div>
+                </div>
+              </van-col>
+              <van-col span="12">
+                <div class="w-[90%] flex text-[#FD9A00]">
+                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/star.png" alt="" />
+                  <div class="font-bold leading-[20px]">推荐指数/</div>
+                  <div class="leading-[20px] flex-1">
+                    {{ defaultNoteJson?.recommendationRate || '' }}
+                  </div>
+                </div>
+              </van-col>
+            </van-row>
+          </div>
+        </div>
+        <div class="mt-10" v-for="con in defaultNoteJson.travelNotesContent" :key="con.id">
+          <template v-if="con.type == 'image'">
+            <img :src="con.content" class="w-[full] rounded-xl" alt="" />
+          </template>
+          <template v-else>
+            <div v-html="con.content"></div>
+          </template>
         </div>
       </div>
-
-      <van-button
-        class="w-full h-full flex items-center"
-        size="large"
-        type="primary"
-        round
-        color="#FD9A00"
-        block
+      <div
+        style="justify-content: space-between"
+        class="fixed box-border p-16 shadow-[0_-4px_4px_0px_rgba(0,0,0,0.1)] bottom-0 left-0 w-full h-80 flex justify-between bg-white items-center"
       >
-        <template #icon>
-          <van-image width="22" height="22" :src="upload" />
-        </template>
-        发布
-      </van-button>
-    </div>
+        <div class="w-165">
+          <van-button
+            @click="handlePreview"
+            style="color: #fd9a00"
+            class="w-full h-full flex items-center"
+            size="large"
+            type="primary"
+            round
+            color="#FFF7E6"
+          >
+            继续编辑
+          </van-button>
+        </div>
 
-    <div class="flex justify-center">
-      <div class="mx-auto mt-30 flex w-wrap space-x-50">
-        <div>
-          <!-- <VueDraggable v-model="noteJson.travelNotesContent">
-              <template
-                v-for="(item, index) in noteJson.travelNotesContent"
-                :key="item.tmpId"
-              >
-                <CreateNoteInsertTitleSection
-                  v-if="item.type === defaultSectionTitle.type"
-                  :title="item.content"
-                  @onEdit="handleInsertOrEditTitle(index)"
-                  @onDelete="handleDeleteTitle(index)"
-                />
-                <template v-else-if="item.type === defaultSectionContent.type">
-                  <CreateNoteInsertContentSection
-                    v-model="item.content"
-                    @on-delete="handleDeleteContent(index)"
-                  />
-                </template>
-                <template v-else-if="item.type === defaultSectionImage.type">
-                  <CreateNoteInsertImageSection
-                    :url="item.content"
-                    @on-save-cover="handleSaveCover(item)"
-                    @on-delete="handleDeleteImage(index)"
-                  />
-                </template>
-              </template>
-            </VueDraggable> -->
-          <!-- <CreateNoteBottomActions
-              class="mt-50"
-              :publishLoading="publishLoading"
-              @on-preview="handlePreview"
-              @on-publish="handlePublish"
-            /> -->
+        <div class="w-165">
+          <van-button
+            class="w-full h-full flex items-center"
+            size="large"
+            type="primary"
+            round
+            color="#FD9A00"
+            block
+            :loading="publishLoading"
+            @click="requestPublish"
+          >
+            <template #icon>
+              <van-image width="22" height="22" :src="upload" />
+            </template>
+            确定发布
+          </van-button>
         </div>
       </div>
     </div>
-
-    <!-- <CreateNoteInsertTitleModal
-      v-model:visible="insertTilteOptions.show"
-      :title="insertTilteOptions.content"
-      @on-ok="handleInsertOrEditTitleOk"
-    /> -->
-    <!-- <CreateNoteInsertImageModal
-      v-model:visible="insertImageOptions.show"
-      @on-ok="handleInsertImageOk"
-    /> -->
-    <!-- <CreateNotePreviewModal
-      v-model:visible="previewOptions.show"
-      :data="noteJson"
-    /> -->
-    <!-- <CreateNoteUserInfoModal
+    <CreateNoteUserInfoModal
       v-model:visible="userInfoOptions.show"
       @submit-ok="handleCollectUserInfoOk"
-    /> -->
-    <!-- <CreateNotePublishResultModal
-      v-model:visible="publishResultModalOptions.show"
-    /> -->
+    />
+    <CreateNotePublishResultModal v-model:visible="publishResultModalOptions" />
   </div>
 </template>
 
 <script setup>
+import { useFileDialog } from '@vueuse/core'
 import upload from '~/assets/img/note-create/upload.svg'
-import { cloneDeep } from 'lodash-es'
-// import { VueDraggable } from 'vue-draggable-plus'
+import draft from '~/assets/img/note-create/draft.svg'
+// import { cloneDeep } from 'lodash-es'
+import { VueDraggable } from 'vue-draggable-plus'
 import { nanoid } from 'nanoid'
+const useAuth = useAuthStore()
+const { token } = storeToRefs(useAuth)
+
+const dragOptions = {
+  disabled: false, // 是否禁用拖拽
+  animation: 150 // 拖拽时的动画效果持续时间
+}
+
+const { open, onChange } = useFileDialog({
+  accept: '.png,.png,.jpeg,.JPG,Png '
+})
 
 const { loading, setLoading } = useLoading()
 loading.value = false
@@ -309,15 +442,42 @@ const noteJson = reactive(defaultNoteJson)
 
 watch(noteJson, () => {}, { deep: true })
 
-// const id = useRouteQuery('id')
+const id = useRouteQuery('id')
+console.log(noteJson.projectTitle, 'projectTitle')
+
+watch(
+  id,
+  () => {
+    getNoteDetail()
+  },
+  { immediate: true }
+)
+
+// 删除的弹窗内容
+const deleteDialogContent = {
+  title: '温馨提示',
+  message: '是否删除',
+  confirmButtonColor: '#FF9300'
+}
+
+// 保存草稿的弹窗内容
+const draftDialogContent = {
+  title: '是否保存到草稿箱',
+  message: '保存后,可在“我的-我的游记-草稿箱”查看',
+  confirmButtonText: '保存草稿',
+  confirmButtonColor: '#FF9300',
+  cancelButtonText: '不保存'
+}
 
-// watch(
-//   id,
-//   () => {
-//     // getNoteDetail()
-//   },
-//   { immediate: true }
-// )
+// 显示弹窗
+const showDialog = (parmas) => {
+  showConfirmDialog(parmas)
+    .then(() => {
+      // getNoteDetail()
+      handleSaveDraft()
+    })
+    .catch(() => {})
+}
 
 // 获取草稿详情
 async function getNoteDetail() {
@@ -352,13 +512,18 @@ const insertTilteOptions = reactive({
   content: null,
   editIndex: null
 })
+
 const editIndex = ref(null)
 
-// 点击编辑或者新增段落标题,弹出dialog
+// 点击那个插入的内容那个就是框的下标
 function handleInsertOrEditTitleIndex(index) {
   editIndex.value = index
 }
 
+// 点击编辑大标题,
+function handleInsertOrEditProjectTitle(value) {
+  noteJson.travelNotesContent.projectTitle = value
+}
 // 点击编辑或者新增段落标题,弹出dialog
 function handleInsertOrEditTitle(value) {
   noteJson.travelNotesContent[editIndex.value].content = value
@@ -366,7 +531,6 @@ function handleInsertOrEditTitle(value) {
 
 // 确认编辑或者新增段落标题
 function handleInsertOrEditTitleOk(newTitle) {
-  // if (insertTilteOptions.editIndex === null) {
   noteJson.travelNotesContent.push({
     ...newTitle,
     tmpId: nanoid()
@@ -375,33 +539,63 @@ function handleInsertOrEditTitleOk(newTitle) {
 
 // 删除段落标题
 function handleDeleteTitle(index) {
-  noteJson.travelNotesContent.splice(index, 1)
+  showConfirmDialog(deleteDialogContent)
+    .then(() => {
+      noteJson.travelNotesContent.splice(index, 1)
+    })
+    .catch(() => {})
 }
 
 /******************插入正文相关逻辑*******************/
 
-function handleInsertContent() {
-  noteJson.travelNotesContent.push(
-    cloneDeep({
-      ...defaultSectionContent,
-      tmpId: nanoid()
-    })
-  )
+const imageLoading = ref(false)
+
+// 单个点击上传图片的事件
+const handleSelectImage = (index) => {
+  handleInsertOrEditTitleIndex(index)
+  open()
 }
 
-// function handleDeleteContent(index) {
-//   noteJson.travelNotesContent.splice(index, 1)
-// }
+onChange((files) => {
+  if (!files.length) return
+  const reader = new FileReader()
+  reader.readAsDataURL(files[0])
+  reader.onload = () => {
+    if (noteJson.travelNotesContent[editIndex.value].type == 'image') {
+      noteJson.travelNotesContent[editIndex.value].content = reader.result
+    }
 
-/******************插入图片逻辑*******************/
-const insertImageOptions = reactive({
-  show: false
+    handleCropperOk(files[0])
+  }
 })
 
-function handleInsertImage() {
-  insertImageOptions.show = true
+async function handleCropperOk(data) {
+  console.log(data, '12222')
+  try {
+    // imageLoading.value = true
+    // 此处需上传图片,保存URL
+    const formData = new FormData()
+    formData.append('uploadFile', data)
+    formData.append('asImage', true)
+    formData.append('fieldName', 'travelNotesBanner')
+
+    console.log(formData, 'formData')
+
+    const res = await request('/admin/app/tourismProjectTravelNotesWrite/upload', {
+      method: 'post',
+      body: formData
+    })
+    const url = res.data.fileUrl
+    if (noteJson.travelNotesContent[editIndex.value].type == 'image') {
+      noteJson.travelNotesContent[editIndex.value].content = url
+    }
+  } finally {
+    // imageLoading.value = false
+  }
 }
 
+/******************插入图片逻辑*******************/
+
 // function handleInsertImageOk(fileUrlList) {
 //   const imageList = fileUrlList.map((e) => ({
 //     type: defaultSectionImage.type,
@@ -412,9 +606,13 @@ function handleInsertImage() {
 // }
 
 function handleDeleteImage(index) {
-  if (noteJson.travelNotesContent[index].type === 'image') {
-    noteJson.travelNotesContent.splice(index, 1)
-  }
+  showConfirmDialog(deleteDialogContent)
+    .then(() => {
+      if (noteJson.travelNotesContent[index].type === 'image') {
+        noteJson.travelNotesContent.splice(index, 1)
+      }
+    })
+    .catch(() => {})
 }
 
 // 设为封面图
@@ -428,48 +626,46 @@ function handleSaveCover(item) {
   showToast('设置封面成功')
 }
 
-const fileList = ref([])
 // 图片上传
 const afterRead = (file) => {
-      // 此时可以自行将文件上传至服务器
-      uploadFile(file)
-      
-
-};
-
-const useAuth = useAuthStore()
-const { token } = storeToRefs(useAuth)
+  // 此时可以自行将文件上传至服务器
+  uploadFile(file)
+}
 
 const uploadUrl = `${import.meta.env.VITE_APP_BASE_URL}/admin/app/tourismProjectTravelNotesWrite/upload`
 
 const uploadFile = (file) => {
   // 使用 FormData 来包装文件,模拟表单提交
-  const formData = new FormData();
-  formData.append('file', file);
+  const formData = new FormData()
+  formData.append('uploadFile', file)
+  formData.append('asImage', true)
+  formData.append('fieldName', 'travelNotesBanner')
   // 这里假设使用 axios 进行文件上传
   request(uploadUrl, formData, {
     headers: {
-      Authorization:token,
+      Authorization: token,
       'Content-Type': 'multipart/form-data'
     }
-  }).then(res => {
-    console.log(res,'img1111');
-    // noteJson.travelNotesContent.map(el=>{
-    //   if (el.type==='image') {
-    //     noteJson.travelNotesContent[editIndex.value].content=''
-    //   }
-    // })
-    
-    
-    // 处理上传成功的逻辑
-    // Toast('上传成功');
-  }).catch(err=> {
-    console.log(err,'img');
-    
-    // 处理上传失败的逻辑
-    // Toast('上传失败,请重试');
-  });
-};
+  })
+    .then((res) => {
+      console.log(res, 'img1111')
+      // noteJson.travelNotesContent.map(el=>{
+      //   if (el.type==='image') {
+      //     noteJson.travelNotesContent[editIndex.value].content=''
+      //   }
+      // })
+
+      // 处理上传成功的逻辑
+      console.log('上传成功')
+    })
+    .catch((err) => {
+      console.log(err, 'img')
+
+      // 处理上传失败的逻辑
+
+      console.log('上传失败,请重试')
+    })
+}
 
 // 保存为草稿
 async function handleSaveDraft() {
@@ -478,7 +674,7 @@ async function handleSaveDraft() {
       method: 'post',
       body: {
         ...noteJson,
-        // id: id?.value
+        id: id?.value
       }
     })
     showToast('草稿保存成功')
@@ -490,8 +686,9 @@ async function handleSaveDraft() {
 const previewOptions = reactive({
   show: false
 })
+
 function handlePreview() {
-  previewOptions.show = true
+  previewOptions.show = !previewOptions.show
 }
 
 // 收集个人信息
@@ -500,12 +697,10 @@ const userInfoOptions = reactive({
 })
 
 function handleCollectUserInfoOk() {
-  // requestPublish()
+  requestPublish()
 }
 
-const publishResultModalOptions = reactive({
-  show: false
-})
+const publishResultModalOptions = ref(false)
 
 // 发布
 async function handlePublish() {
@@ -543,27 +738,32 @@ async function handlePublish() {
     // 需要收集个人信息
     userInfoOptions.show = true
   } else {
-    // requestPublish()
+    requestPublish()
   }
 }
 
 const publishLoading = ref(false)
-// async function requestPublish() {
-//   try {
-//     publishLoading.value = true
-//     await request('/website/tourism/publishTravelNotes/publishDraft', {
-//       method: 'post',
-//       body: {
-//         ...noteJson,
-//         id: id.value
-//       }
-//     })
-//     publishResultModalOptions.show = true
-//     publishLoading.value = false
-//   } catch (error) {
-//     publishLoading.value = false
-//   }
-// }
+async function requestPublish() {
+  try {
+    publishLoading.value = true
+    await request('/website/tourism/publishTravelNotes/publishDraft', {
+      method: 'post',
+      body: {
+        ...noteJson,
+        id: id.value
+      }
+    })
+    publishResultModalOptions.value = true
+    publishLoading.value = false
+  } catch (error) {
+    publishLoading.value = false
+  }
+}
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+::v-deep .van-field:focus {
+  caret-color: #ff9300 !important;
+  outline: none;
+}
+</style>

+ 3 - 3
src/pages/profile/index/notes.vue → src/pages/profile/notes/index.vue

@@ -1,10 +1,10 @@
 <template>
-  <div ref="container" class="w-full h-[200vh] box-border pb-16 px-20 bg-[#F8F8F8]">
-    <van-sticky :offset-top="0" :container="container">
+  <div ref="container" class="w-full box-border pb-16 px-20 bg-[#F8F8F8]">
+    <van-sticky :offset-top="50" :container="container">
       <ProfileNotesTabs :tabs="tabs"></ProfileNotesTabs>
     </van-sticky>
 
-    <div class="w-full h-full">
+    <div class="w-full h-[200vh]">
       <van-pull-refresh v-model="loading" @refresh="onRefresh">
         <div v-if="tab === 'published'">
           <ProfileNotesPublished />

Some files were not shown because too many files changed in this diff