createOrder.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <template>
  2. <div class="store-page service-page">
  3. <div class="service-page__inner service-page__inner--wide">
  4. <Breadcrumb />
  5. <header class="page-header">
  6. <div>
  7. <h1 class="store-section-title">提交工单</h1>
  8. <p class="store-section-desc">选择常见问题模板,或自行填写后提交,客服将尽快回复</p>
  9. </div>
  10. <a-button type="outline" @click="$router.push('/service/orderList')">
  11. <template #icon><icon-list /></template>
  12. 我的工单
  13. </a-button>
  14. </header>
  15. <a-card :bordered="false" class="form-card">
  16. <WorkOrderTemplatePicker
  17. v-model="selectedTemplateId"
  18. @select="applyTemplate"
  19. />
  20. <a-form :model="form" :rules="rules" layout="vertical" @submit-success="handleSubmit">
  21. <a-form-item field="title" label="工单标题">
  22. <a-input v-model="form.title" :max-length="25" allow-clear show-word-limit placeholder="简要概括您的问题" />
  23. </a-form-item>
  24. <a-form-item field="content" label="问题描述">
  25. <a-textarea
  26. v-model="form.content"
  27. placeholder="请详细说明您遇到的问题,信息越完整越便于我们快速处理"
  28. :max-length="500"
  29. :auto-size="{ minRows: 8, maxRows: 16 }"
  30. allow-clear
  31. show-word-limit
  32. />
  33. </a-form-item>
  34. <a-form-item field="files" label="上传附件(选填)">
  35. <a-upload action="/cloud/api.php" :file-list="form.files" @change="handleFileChange" />
  36. </a-form-item>
  37. <a-form-item field="email" label="通知邮箱">
  38. <EmailAutoComplete
  39. v-model="form.email"
  40. :max-length="30"
  41. allow-clear
  42. placeholder="客服回复后将通过邮件通知您"
  43. />
  44. </a-form-item>
  45. <a-alert v-if="!hasPermission('action.service.createOrder')" type="warning" class="perm-alert">
  46. 当前账号暂无发起工单权限
  47. </a-alert>
  48. <a-form-item>
  49. <a-space>
  50. <a-button
  51. type="primary"
  52. html-type="submit"
  53. size="large"
  54. class="submit-btn"
  55. :loading="loading"
  56. :disabled="!hasPermission('action.service.createOrder')"
  57. >
  58. 提交工单
  59. </a-button>
  60. <a-button size="large" @click="clearForm">清空内容</a-button>
  61. </a-space>
  62. </a-form-item>
  63. </a-form>
  64. </a-card>
  65. </div>
  66. </div>
  67. </template>
  68. <script setup>
  69. import { ref, reactive } from 'vue'
  70. import { createOrder } from '@/api/workOrder'
  71. import { useRouter } from 'vue-router'
  72. import { Notification } from '@arco-design/web-vue'
  73. import { hasPermission } from '@/utils/permission'
  74. import WorkOrderTemplatePicker from '@/components/service/WorkOrderTemplatePicker.vue'
  75. const loading = ref(false)
  76. const router = useRouter()
  77. const selectedTemplateId = ref('')
  78. const rules = {
  79. title: [{ required: true, message: '请输入工单标题' }],
  80. content: [{ required: true, message: '请详细说明您遇到的问题' }],
  81. email: [{ type: 'email', required: true, message: '请填写正确的通知邮箱' }]
  82. }
  83. const form = reactive({
  84. title: '',
  85. content: '',
  86. email: '',
  87. files: []
  88. })
  89. const applyTemplate = (item) => {
  90. form.title = item.title
  91. form.content = item.content
  92. }
  93. const clearForm = () => {
  94. selectedTemplateId.value = ''
  95. form.title = ''
  96. form.content = ''
  97. form.files = []
  98. }
  99. const handleSubmit = async () => {
  100. if (!hasPermission('action.service.createOrder')) {
  101. Notification.warning({ title: '无权限', content: '当前账号暂无发起工单权限' })
  102. return
  103. }
  104. try {
  105. loading.value = true
  106. const res = await createOrder(form)
  107. if (!res || res.code !== 0) {
  108. return Notification.error({
  109. title: '提交失败',
  110. content: res?.msg ?? '请稍后再试'
  111. })
  112. }
  113. Notification.success({ title: '提交成功', content: '我们已收到您的工单' })
  114. router.push(`/service/orderDetail/${res.data}`)
  115. } catch (error) {
  116. Notification.error({
  117. title: '提交失败',
  118. content: error.message || '请稍后再试'
  119. })
  120. } finally {
  121. loading.value = false
  122. }
  123. }
  124. const handleFileChange = (fileList) => {
  125. fileList.forEach((f) => {
  126. if (f.status === 'done' && f.response?.downurl) {
  127. f.url = f.response?.viewurl ?? f.response.downurl
  128. }
  129. })
  130. form.files = fileList
  131. }
  132. </script>
  133. <style lang="less" scoped>
  134. @import '@/styles/store-theme.less';
  135. .page-header {
  136. display: flex;
  137. flex-wrap: wrap;
  138. align-items: flex-end;
  139. justify-content: space-between;
  140. gap: 16px;
  141. margin-bottom: 20px;
  142. }
  143. .form-card {
  144. border-radius: @store-radius;
  145. border: 1px solid @store-card-border;
  146. box-shadow: @store-shadow;
  147. padding: 8px 4px 4px;
  148. }
  149. .perm-alert {
  150. margin-bottom: 16px;
  151. border-radius: @store-radius-sm;
  152. }
  153. .submit-btn {
  154. border-radius: 999px;
  155. background: @store-primary !important;
  156. border-color: @store-primary !important;
  157. }
  158. </style>