#Vue JS 前端問題 # 前提 網站URL https://starrocket-cms-staging.starrocket.io/ 登入帳號: scott.hsiao@starrocket.io Ss111111 問題網頁 https://starrocket-cms-staging.starrocket.io/course/list # 問題 資料表的標題目前是跳轉使用者前端的預覽,想改成跳轉行動的編輯 節錄 index.js 標題欄位 ``` data() { return { columns: [ { name: 'id', label: 'ID', align: 'center', format: (val) => val, sortable: true, }, ... { name: 'title', label: '標題', align: 'left', format: (val) => `<a href="${process.env.URL}/course/${val.category.id}/${val.id}" target="_blank" rel="noreferrer noopener">${val.title}</a>`, html: true, fullData: true, sortable: true, }, ``` 跳轉編輯的部分 ``` dataView(row) { this.$router.push({ name: 'course/content', params: { id: row.id, }, }); }, ``` 完整內容 index.js ``` import moment from 'moment'; import MyTable from '@/components/MyTable/index.vue'; export default { name: 'PageCourseList', components: { MyTable, }, data() { return { columns: [ { name: 'id', label: 'ID', align: 'center', format: (val) => val, sortable: true, }, { name: 'image', label: '圖片', align: 'center', format: (val) => `<img src="${val}" width="80" height="45">`, html: true, sortable: false, }, { name: 'category', label: '分類', align: 'left', format: (val) => (val ? val.name : ''), sortable: true, }, { name: 'title', label: '標題', align: 'left', format: (val) => `<a href="${process.env.URL}/course/${val.category.id}/${val.id}" target="_blank" rel="noreferrer noopener">${val.title}</a>`, html: true, fullData: true, sortable: true, }, { name: 'created_at', label: '課程建立時間', align: 'left', format: (val) => val, sortable: true, }, { name: 'sale_start_at', label: '售票開始時間', align: 'left', format: (val) => moment(val).format('YYYY-MM-DD HH:mm'), sortable: true, }, { name: 'sale_end_at', label: '售票結束時間', align: 'left', format: (val) => moment(val).format('YYYY-MM-DD HH:mm'), sortable: true, }, { name: 'start_at', label: '課程開始時間', align: 'left', format: (val) => moment(val).format('YYYY-MM-DD HH:mm'), sortable: true, }, { name: 'end_at', label: '課程結束時間', align: 'left', format: (val) => moment(val).format('YYYY-MM-DD HH:mm'), sortable: true, }, { name: 'frontend_status', label: '前台狀態', align: 'left', format: (val) => ['上架', '下架'][val], sortable: true, }, { name: 'sale_status', label: '售票狀態', align: 'left', format: (val) => ['即將上線', '報名中', '額滿', '停止售票', '結束'][val], sortable: true, }, ], data: [], filter: { sort_by: 'id', descending: true, page: 1, rows_per_page: 50, category: null, keyword: '', sale_status: null, }, actions: [ { color: 'accent', label: (row) => { return row.is_top ? '下置頂' : '上置頂'; }, show: (row) => { return this.canEdit; }, click: this.dataIsTopChange, }, ], total: 0, categoryData: [], categoryOption: [ { value: null, label: '全部', }, ], StatusOption: [ { value: null, label: '全部', }, { value: 0, label: '即將上線', }, { value: 1, label: '報名中', }, { value: 2, label: '額滿', }, { value: 3, label: '停止售票', }, { value: 4, label: '結束', }, ], keywordInputTimer: null, // * 0: 即將上線 / 前台: 不會出現 // * 1: 報名中 / 前台: 立刻報名 // * 2: 額滿 / 前台: 額滿 // * 3: 停止售票 / 前台: 停止售票 // * 4: 結束 / 前台: 結束 // * 5: 下線(下架) / 前台: 不會出現 }; }, computed: {}, mounted() { if (!this.canView) { return; } const listFilterStr = sessionStorage.getItem('listFilter'); if (listFilterStr) { const listFilter = JSON.parse(listFilterStr); if (listFilter.name === this.$route.name) { this.filter = listFilter.filter; } } this.getData(); this.getTypeData(); }, methods: { async getData() { const resp = await this.$services.admin.course.getList(this.filter); this.total = resp.data.total; this.data = resp.data.data; if (this.data.length === 0 && this.filter.page > 1) { this.filter.page--; this.getData(); return; } sessionStorage.setItem( 'listFilter', JSON.stringify({ name: this.$route.name, filter: this.filter, }), ); }, async addCourse() { const resp = await this.$services.admin.course.post(); this.$router.push({ name: 'course/content', params: { id: resp.data.id, }, }); }, onRowsPerPageChange() { this.filter.page = 1; this.getData(); }, dataView(row) { this.$router.push({ name: 'course/content', params: { id: row.id, }, }); }, async dataStatusChange(row) { await this.$services.admin.course.edit(row.id, { frontend_status: !row.frontend_status ? 1 : 0, }); this.$lib.notify.success('儲存成功'); this.getData(); }, async dataIsTopChange(row) { await this.$services.admin.course.edit(row.id, { is_top: !row.is_top ? 1 : 0, }); this.$lib.notify.success('儲存成功'); this.getData(); }, async dataDelete(row) { await this.$services.admin.course.delete(row.id); this.$lib.notify.success('刪除成功'); this.getData(); }, async dataOffline(row) { await this.$services.admin.course.edit(row.id, { status: 5, }); this.$lib.notify.success('下線成功'); this.getData(); }, async getTypeData() { const resp = await this.$services.admin.course.type.getList(); this.categoryData = resp.data; this.categoryData.forEach((item, index, array) => { this.categoryOption.push({ label: item.name, value: item.id }); }); }, async onSelectCategory() { const resp = await this.$services.admin.course.getList(this.filter); this.total = resp.data.total; this.data = resp.data.data; }, async onSelectStatus() { const resp = await this.$services.admin.course.getList(this.filter); this.total = resp.data.total; this.data = resp.data.data; }, onKeywordInput() { if (this.keywordInputTimer) { clearTimeout(this.keywordInputTimer); } this.keywordInputTimer = setTimeout(() => { this.filter.page = 1; this.getData(); }, 500); }, }, }; ``` index.vue ``` <template> <div> <div class="row justify-between items-center q-pb-sm"> <div class="col-auto"> <div class="row items-center"> <div class="col-auto"> <div class="text-h6">課程列表</div> </div> <div class="col-auto"> <q-btn no-caps outline size="md" color="primary" label="新增" class="q-mx-sm" v-if="canEdit" @click="addCourse" /> </div> </div> </div> <div class="col-auto"> <Breadcrumbs /> </div> </div> <q-card flat> <q-card-section class="q-py-sm"> <div class="row items-center justify-between"> <div class="col-auto"> <div class="row items-center"> <div class="col-auto"> <q-select dense outlined v-model="filter.rows_per_page" :options="[5, 10, 20, 30, 40, 50]" @input="onRowsPerPageChange" > <template v-slot:before> <div class="text-body2">顯示</div> </template> <template v-slot:after> <div class="text-body2">筆資料</div> </template> </q-select> </div> </div> </div> <div class="col-auto"> <div class="row items-center"> <div class="col-auto q-ml-sm q-px-sm"> <q-select dense outlined option-value="value" emit-value map-options v-model="filter.category" :options="categoryOption" @input="onSelectCategory" > <template v-slot:before> <div class="text-body2">依分類查詢</div> </template> </q-select> </div> <div class="col-auto q-ml-sm q-px-sm"> <q-select dense outlined option-value="value" emit-value map-options v-model="filter.sale_status" :options="StatusOption" @input="onSelectStatus" > <template v-slot:before> <div class="text-body2">依狀態查詢</div> </template> </q-select> </div> <div class="col-auto q-ml-md"> <q-input outlined dense v-model="filter.keyword" label="關鍵字查詢" hide-bottom-space @input="onKeywordInput" > <template v-slot:before> <div class="text-body2">依標題查詢</div> </template> </q-input> </div> </div> </div> </div> </q-card-section> <q-card-section class="q-py-sm"> <MyTable :columns="columns" :data="data" :actions="actions" :filter="filter" :total="total" :actionView="true" :actionStatus="false" :actionDelete="true" :actionOffline="false" :actionFrontendStatus="true" actionViewRouter="course/content" @getData="getData" @dataView="dataView" @dataStatusChange="dataStatusChange" @dataDelete="dataDelete" @dataOffline="dataOffline" /> </q-card-section> </q-card> </div> </template> <script src="./index.js"></script> ```