Create.vue 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798
  1. <script setup>
  2. import { ref, reactive, watch, computed, onMounted } from "vue";
  3. import { useMainStore } from "@/stores/store";
  4. import { Loader } from "@googlemaps/js-api-loader";
  5. import VueDatePicker from "@vuepic/vue-datepicker";
  6. import "@vuepic/vue-datepicker/dist/main.css";
  7. import axios from "axios";
  8. import moment from "moment";
  9. import Navbar from "@/components/Navbar.vue";
  10. const store = useMainStore();
  11. const token = store.token;
  12. console.log("token", token);
  13. let step = ref(1);
  14. let stepTitle = ref("");
  15. let stepDescription = ref("");
  16. let loading = ref(false);
  17. const computedTitle = computed(() => {
  18. switch (step.value) {
  19. case 1:
  20. stepTitle.value = "Step1 新增據點";
  21. stepDescription.value = "創建課程之前,請先新增您的據點(上課地址)";
  22. break;
  23. case 2:
  24. stepTitle.value = "Step2 工藝家教學履歷";
  25. stepDescription.value =
  26. "開始打造屬於您的工藝履歷,讓學徒對你印象深刻吧!";
  27. break;
  28. case 3:
  29. stepTitle.value = "Step3 創建課程";
  30. stepDescription.value =
  31. "完整且清楚的課程建立,是連接工藝老師與學徒的重要橋梁!";
  32. break;
  33. case 4:
  34. stepTitle.value = "恭喜您完成創建課程!";
  35. stepDescription.value = "";
  36. break;
  37. }
  38. return { stepTitle: stepTitle.value, stepDescription: stepDescription.value };
  39. });
  40. let date = reactive({
  41. start_date: "", // 起始日期
  42. start_time: "", // 起始時間
  43. end_date: "", // 結束日期
  44. end_time: "", // 結束時間
  45. registration_start_date: "", // 報名起始日期
  46. registration_start_time: "", // 報名起始時間
  47. registration_end_date: "", // 報名截止日期
  48. registration_end_time: "", // 報名截止時間
  49. });
  50. // 處理時間格式(日期+時間)
  51. function mergeAndFormatDateTime(dateString, timeInfo) {
  52. const date = new Date(dateString);
  53. date.setHours(timeInfo.hours);
  54. date.setMinutes(timeInfo.minutes);
  55. date.setSeconds(timeInfo.seconds);
  56. const year = date.getUTCFullYear();
  57. const month = String(date.getUTCMonth() + 1).padStart(2, "0");
  58. const day = String(date.getUTCDate()).padStart(2, "0");
  59. const hours = String(date.getUTCHours()).padStart(2, "0");
  60. const minutes = String(date.getUTCMinutes()).padStart(2, "0");
  61. const seconds = String(date.getUTCSeconds()).padStart(2, "0");
  62. return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.000Z`;
  63. }
  64. const breadcrumbs = reactive([
  65. {
  66. title: "首頁",
  67. disabled: false,
  68. href: "/",
  69. },
  70. {
  71. title: "我要開課",
  72. disabled: false,
  73. href: "/setup-courses",
  74. },
  75. {
  76. title: "創建課程",
  77. disabled: true,
  78. },
  79. ]);
  80. // Google Map
  81. const states = reactive({
  82. google: null,
  83. map: null,
  84. markers: null,
  85. });
  86. const initMap = async () => {
  87. const loader = new Loader({
  88. apiKey: "AIzaSyAzDeviZ-TpwzT1atlnshNJRjBgndP05Mw",
  89. version: "weekly",
  90. libraries: ["places"],
  91. language: "zh-TW",
  92. });
  93. states.google = await loader.load();
  94. states.map = new states.google.maps.Map(document.getElementById("map"), {
  95. center: { lat: 25.0425, lng: 121.5468 },
  96. zoom: 11,
  97. mapTypeControl: false,
  98. fullscreenControl: false,
  99. });
  100. };
  101. onMounted(async () => {
  102. await initMap();
  103. });
  104. let location = reactive({
  105. location_name: "",
  106. Lng: "",
  107. Lat: "",
  108. address: "",
  109. introduction: "",
  110. email: "",
  111. phone: "",
  112. access_token: token,
  113. });
  114. let locationId = ref("");
  115. async function insertSchool() {
  116. // 若已選擇據點則不需新增
  117. if (schoolId.value !== "") {
  118. locationId.value = schoolId.value;
  119. return;
  120. }
  121. const formData = new FormData();
  122. for (const key in location) {
  123. formData.append(key, location[key]);
  124. }
  125. try {
  126. const response = await axios.post(
  127. "https://cmm.ai:8088/api/insert_school",
  128. formData
  129. );
  130. locationId.value = response.data.location_id;
  131. console.log("新增據點 response", response);
  132. } catch (error) {
  133. console.error(error);
  134. }
  135. }
  136. // 新增履歷
  137. let resume = reactive({
  138. teacher_name: "", // 老師姓名
  139. work_type: "", // 工作性質
  140. experience: "", // 教學經驗
  141. expertise: "", // 專長工藝技能
  142. license: "", // 工藝相關證照
  143. media: "", // 社群媒體
  144. files: [], // 作品集
  145. introduction: "", // 老師介紹
  146. });
  147. let oldFile = "";
  148. async function insertUserResume() {
  149. if (portfolioImg.value.length) {
  150. resume.files = portfolioImg.value;
  151. }
  152. const formData = new FormData();
  153. for (const key in resume) {
  154. if (key === "files") {
  155. resume.files.forEach((file) => {
  156. formData.append("files", file);
  157. });
  158. } else {
  159. formData.append(key, resume[key]);
  160. }
  161. }
  162. let token = store.token;
  163. try {
  164. const response = await axios.post(
  165. `https://cmm.ai:8088/api/input_user_resume?access_token=${token}&old_file=${oldFile}`,
  166. formData
  167. );
  168. console.log("新增履歷 response", response);
  169. } catch (error) {
  170. console.error(error);
  171. }
  172. }
  173. // 新增課程
  174. let course = reactive({
  175. name: "", // 課程名稱
  176. location_id: "", // 據點編號
  177. category: "", // 工藝類別
  178. introduction: "", // 課程簡介
  179. organizer: "", // 主辦單位
  180. cover_img_file: "", // 課程圖片
  181. group_id: 2, // 學群編號(技藝)
  182. group_sort: "", // 學群細分
  183. special_class_list_name: "",
  184. recommend: 0, // 是否推薦
  185. is_inner: 1, // 內課課程
  186. is_check: 0, // 審核結果
  187. access_token: token,
  188. });
  189. watch(location, (data) => {
  190. course.organizer = data.location_name; // 據點名稱 = 主辦單位
  191. });
  192. let classNameId = ref("");
  193. async function insertClassName() {
  194. console.log("insertClassName", course);
  195. course.location_id = locationId.value;
  196. if (coverImg.value !== "") {
  197. course.cover_img_file = coverImg.value;
  198. }
  199. const formData = new FormData();
  200. for (const key in course) {
  201. formData.append(key, course[key]);
  202. }
  203. try {
  204. const response = await axios.post(
  205. "https://cmm.ai:8088/api/insert_class_name",
  206. formData
  207. );
  208. console.log("新增課程 response", response);
  209. classNameId.value = response.data.new_class_name_id;
  210. } catch (error) {
  211. console.error(error);
  212. }
  213. }
  214. // 新增場次
  215. let event = reactive({
  216. name_id: "", // 課程名稱編號(需先新增課程取得Id)
  217. event: "", // 場次名稱
  218. start_time: "", // 課程起始日
  219. end_time: "", // 課程結束日
  220. contact: "", // 聯絡資訊
  221. lecturer: "", // 課程講師
  222. location: "",
  223. content: "", // 課程內容
  224. URL: "", // 外部連結
  225. people: "不拘", // 對象
  226. fee_method: "", // 課程價格
  227. fee_payment: "", // 收費方式
  228. registration_way: "", // 報名方式
  229. registration_start: "", // 報名起始日
  230. registration_end: "", // 報名截止日
  231. number_limit: "", // 課程名額
  232. remark: "", // 備註
  233. ATM_address: "", // 匯款帳號(銀行代碼+帳號)
  234. access_token: token,
  235. });
  236. let eventType = ref("");
  237. let isOneDay = ref(true);
  238. watch(eventType, (val) => {
  239. if (val === "週期課程(例:2023/10/1~2023/10/30 每週一三上課)") {
  240. isOneDay.value = false;
  241. } else {
  242. isOneDay.value = true;
  243. }
  244. });
  245. let eventData = reactive({
  246. list: [],
  247. });
  248. let isRemit = ref(false); // 是否顯示匯款資訊欄位
  249. let bankCode = ref("");
  250. let bankAccount = ref("");
  251. watch(event, (data) => {
  252. if (data.fee_payment === "匯款") {
  253. isRemit.value = true;
  254. } else {
  255. isRemit.value = false;
  256. }
  257. });
  258. function addEventData() {
  259. // insertSession(); // 測試
  260. // 處理時間格式
  261. event.start_time = mergeAndFormatDateTime(date.start_date, date.start_time);
  262. event.end_time = mergeAndFormatDateTime(date.end_date, date.end_time);
  263. event.registration_start = mergeAndFormatDateTime(
  264. date.registration_start_date,
  265. date.registration_start_time
  266. );
  267. event.registration_end = mergeAndFormatDateTime(
  268. date.registration_end_date,
  269. date.registration_end_time
  270. );
  271. if (isRemit && bankCode.value !== "" && bankAccount.value !== "") {
  272. event.ATM_address = `(${bankCode.value})-${bankAccount.value}`;
  273. }
  274. eventData.list.push(JSON.parse(JSON.stringify(event)));
  275. sessionsDialog.value = false;
  276. }
  277. function convertDateFormat(inputDateStr) {
  278. const inputDate = new Date(inputDateStr);
  279. const formattedDate = inputDate.toISOString();
  280. return formattedDate;
  281. }
  282. let eventId = ref(null);
  283. async function insertEvent() {
  284. console.log("insertEvent", event);
  285. if (classNameId.value !== "") {
  286. event.name_id = classNameId.value;
  287. }
  288. event.start_time = convertDateFormat(event.start_time);
  289. event.end_time = convertDateFormat(event.end_time);
  290. event.registration_start = convertDateFormat(event.registration_start);
  291. event.registration_end = convertDateFormat(event.registration_end);
  292. console.log("檢查日期格式", event);
  293. const formData = new FormData();
  294. for (const key in event) {
  295. formData.append(key, event[key]);
  296. }
  297. try {
  298. const response = await axios.post(
  299. "https://cmm.ai:8088/api/insert_event",
  300. formData
  301. );
  302. eventId.value = response.data.class_id;
  303. console.log("eventId.value", eventId.value);
  304. console.log("新增場次 response", response);
  305. } catch (error) {
  306. console.error(error);
  307. }
  308. }
  309. // 創建課程
  310. async function create() {
  311. loading.value = true;
  312. try {
  313. await insertSchool(); // 新增據點
  314. await insertUserResume(); // 新增履歷
  315. await insertClassName(); // 新增課程
  316. await insertEvent(); // 新增場次
  317. await insertSession(); // 新增課堂
  318. loading.value = false;
  319. step.value++;
  320. } catch (error) {
  321. console.error(error);
  322. loading.value = false;
  323. }
  324. }
  325. const getCoordinates = async () => {
  326. const geocoder = new states.google.maps.Geocoder();
  327. geocoder.geocode({ address: location.address }, (results, status) => {
  328. if (status === states.google.maps.GeocoderStatus.OK) {
  329. location.Lat = results[0].geometry.location.lat();
  330. location.Lng = results[0].geometry.location.lng();
  331. }
  332. // 將地圖中心設為取得的經緯度,並調整縮放等級
  333. states.map.setCenter({ lat: location.Lat, lng: location.Lng });
  334. states.map.setZoom(15);
  335. // 設定地址圖標
  336. const marker = new google.maps.Marker({
  337. position: { lat: location.Lat, lng: location.Lng },
  338. map: states.map,
  339. title: location.address,
  340. icon: store.getImageUrl("map-icon/icon_house05.png"),
  341. });
  342. });
  343. };
  344. // 上傳圖片 Input
  345. const portfolioImgRef = ref(null);
  346. const coverImgRef = ref(null);
  347. const fileInputClick = (ref) => {
  348. if (ref === "portfolio") {
  349. portfolioImgRef.value.click();
  350. } else if (ref === "cover") {
  351. coverImgRef.value.click();
  352. }
  353. };
  354. // 作品集圖片
  355. let portfolioImg = ref([]);
  356. let portfolioImgList = ref([]);
  357. const handlePortfolioImg = (files) => {
  358. console.log("files", files);
  359. // portfolioImgList.value = []; // 清空陣列
  360. for (let index = 0; index < files.length; index++) {
  361. const file = files[index];
  362. console.log("file", file);
  363. let url = URL.createObjectURL(file);
  364. portfolioImgList.value.push(url);
  365. console.log("portfolioImgList", portfolioImgList.value);
  366. }
  367. };
  368. watch(portfolioImg, (newFiles) => {
  369. if (newFiles.length > 0) {
  370. handlePortfolioImg(newFiles);
  371. }
  372. });
  373. // 封面圖片
  374. let coverImg = ref("");
  375. let coverImgUrl = ref("");
  376. const handleCoverImg = (event) => {
  377. const file = event.target.files[0];
  378. console.log("選擇檔案", file);
  379. if (file) {
  380. coverImg.value = file;
  381. coverImgUrl.value = URL.createObjectURL(file); // 設定圖片預覽 URL
  382. }
  383. };
  384. let sessionsDialog = ref(false);
  385. const requiredRule = (value) => !!value || "此欄位為必填";
  386. const weekList = reactive(["一", "二", "三", "四", "五", "六", "日"]);
  387. const selectedWeek = ref([0, 0, 0, 0, 0, 0, 0]); // 選擇星期
  388. const toggleSelected = (index) => {
  389. if (selectedWeek.value[index]) {
  390. selectedWeek.value[index] = 0;
  391. } else {
  392. selectedWeek.value[index] = 1;
  393. }
  394. };
  395. let session = reactive({
  396. class_event_id: null,
  397. week_day_str: "",
  398. start_week_time: "",
  399. end_week_time: "",
  400. });
  401. let sessionTime = reactive({
  402. start_week_time: [],
  403. end_week_time: [],
  404. });
  405. watch(sessionTime, (val) => {
  406. console.log("sessionTime start_week_time", val.start_week_time);
  407. console.log("sessionTime end_week_time", val.end_week_time);
  408. });
  409. let sessionStartTimeList = ref(["", "", "", "", "", "", ""]);
  410. let sessionEndTimeList = ref(["", "", "", "", "", "", ""]);
  411. // 新增課堂
  412. async function insertSession() {
  413. console.log("isOneDay.value", isOneDay.value);
  414. const weekString = `[${selectedWeek.value.join(",")}]`;
  415. session.week_day_str = weekString;
  416. console.log("weekString", weekString);
  417. selectedWeek.value.map((item, index) => {
  418. if (item) {
  419. console.log("index", index);
  420. // 開始時間
  421. sessionStartTimeList.value[index] = `"${
  422. sessionTime.start_week_time[index].hours < 10 ? "0" : ""
  423. }${sessionTime.start_week_time[index].hours}:${
  424. sessionTime.start_week_time[index].minutes === 0
  425. ? "00"
  426. : sessionTime.start_week_time[index].minutes
  427. }:00"`;
  428. // 結束時間
  429. sessionEndTimeList.value[index] = `"${
  430. sessionTime.end_week_time[index].hours
  431. }:${
  432. sessionTime.end_week_time[index].minutes === 0
  433. ? "00"
  434. : sessionTime.end_week_time[index].minutes
  435. }:00"`;
  436. } else {
  437. sessionStartTimeList.value[index] = '""';
  438. sessionEndTimeList.value[index] = '""';
  439. }
  440. });
  441. session.start_week_time = `[${sessionStartTimeList.value}]`;
  442. session.end_week_time = `[${sessionEndTimeList.value}]`;
  443. session.class_event_id = eventId.value;
  444. // const formData = new FormData();
  445. // for (const key in session) {
  446. // formData.append(key, session[key]);
  447. // }
  448. let oneDayData = {
  449. class_event_id: session.class_event_id,
  450. start_time: event.start_time,
  451. end_time: event.end_time,
  452. content: "",
  453. };
  454. console.log("oneDayData", oneDayData);
  455. // const oneDayFormData = new FormData();
  456. // for (const key in oneDayData) {
  457. // formData.append(key, oneDayData[key]);
  458. // }
  459. try {
  460. if (isOneDay.value) {
  461. const oneDayFormData = new FormData();
  462. for (const key in oneDayData) {
  463. oneDayFormData.append(key, oneDayData[key]);
  464. }
  465. // 一日課程
  466. const response = await axios.post(
  467. "https://cmm.ai:8088/api/insert_session",
  468. oneDayFormData
  469. );
  470. console.log("新增課堂(一日) response", response);
  471. } else {
  472. const formData = new FormData();
  473. for (const key in session) {
  474. formData.append(key, session[key]);
  475. }
  476. // 週期課程
  477. const response = await axios.post(
  478. "https://cmm.ai:8088/api/auto_create_session",
  479. formData
  480. );
  481. console.log("新增課堂(週期) response", response);
  482. }
  483. // const response = await axios.post(
  484. // "https://cmm.ai:8088/api/auto_create_session",
  485. // formData
  486. // );
  487. // console.log("新增課堂 response", response);
  488. } catch (error) {
  489. console.error(error);
  490. }
  491. }
  492. let schoolSelect = reactive({
  493. list: [],
  494. });
  495. let schoolId = ref("");
  496. // 取得已有據點
  497. (async () => {
  498. let token = store.token;
  499. try {
  500. const response = await axios.get(
  501. `https://cmm.ai:8088/api/get_school?access_token=${token}`
  502. );
  503. schoolSelect.list = response.data.schools;
  504. console.log("取得使用者據點", schoolSelect.list);
  505. } catch (error) {
  506. console.error(error);
  507. }
  508. })();
  509. // 監聽據點 Select
  510. watch(schoolId, (id) => {
  511. handleSchoolData(id);
  512. });
  513. let schools = reactive({
  514. list: [],
  515. });
  516. // 帶入據點資料
  517. async function handleSchoolData(id) {
  518. console.log("handleSchoolData", id);
  519. try {
  520. const response = await axios.get(
  521. `https://cmm.ai:8088/api/get_school?location_id=${id}`
  522. );
  523. schools.list = response.data.schools;
  524. console.log("取得已有據點", schools.list);
  525. for (const key in schools.list[0]) {
  526. if (location.hasOwnProperty(key) && schools.list[0][key] !== undefined) {
  527. location[key] = schools.list[0][key];
  528. }
  529. }
  530. } catch (error) {
  531. console.error(error);
  532. }
  533. }
  534. // 取得已有履歷
  535. (async () => {
  536. let token = store.token;
  537. try {
  538. const response = await axios.get(
  539. `https://cmm.ai:8088/api/get_user_resume?access_token=${token}`
  540. );
  541. let userResume = response.data.user_resume;
  542. console.log("已有履歷", userResume);
  543. for (const key in userResume) {
  544. if (resume.hasOwnProperty(key) && userResume[key] !== undefined) {
  545. resume[key] = userResume[key];
  546. }
  547. if (key === "imgs") {
  548. // 存入已有圖片
  549. oldFile = userResume[key];
  550. // 顯示已上傳圖片
  551. let json = JSON.parse(userResume[key].replace(/'/g, '"'));
  552. json.map((item) => {
  553. console.log(
  554. "`https://ntcri.org/${item}`",
  555. `https://ntcri.org/${item}`
  556. );
  557. portfolioImgList.value.push(`https://ntcri.org/${item}`);
  558. });
  559. }
  560. }
  561. console.log("取得已有履歷", userResume);
  562. } catch (error) {
  563. console.error(error);
  564. }
  565. })();
  566. </script>
  567. <template>
  568. <Navbar />
  569. <v-container class="mb-16 pb-16">
  570. <v-breadcrumbs
  571. :items="breadcrumbs"
  572. divider="/"
  573. class="py-10"
  574. ></v-breadcrumbs>
  575. <v-card class="mx-auto pa-10">
  576. <v-card-title class="step-title">
  577. <h5>{{ computedTitle.stepTitle }}</h5>
  578. <p class="mt-5">{{ computedTitle.stepDescription }}</p>
  579. </v-card-title>
  580. <v-window v-model="step">
  581. <v-window-item :value="1">
  582. <v-card-text>
  583. <div v-if="schoolSelect.list.length" class="mb-10 school-list">
  584. <v-label class="d-flex align-center w-100 overflow-visible">
  585. <p class="pe-3">已經有據點?</p>
  586. <v-select
  587. v-model="schoolId"
  588. label="請選擇據點"
  589. :items="schoolSelect.list"
  590. item-title="location_name"
  591. item-value="location_id"
  592. hide-details
  593. density="compact"
  594. variant="outlined"
  595. ></v-select>
  596. </v-label>
  597. </div>
  598. <v-label class="d-flex align-center pb-3">
  599. <p class="pb-5 pe-3">據點名稱<span class="mark">*</span></p>
  600. <v-text-field
  601. v-model="location.location_name"
  602. :rules="[requiredRule]"
  603. placeholder="可以是您的工作室或品牌名稱/教學單位/您的姓名"
  604. density="compact"
  605. variant="outlined"
  606. counter
  607. maxlength="60"
  608. ></v-text-field>
  609. </v-label>
  610. <v-label class="d-flex align-center pb-3 w-100">
  611. <p class="pb-5 pe-3">據點地址<span class="mark">*</span></p>
  612. <v-text-field
  613. v-model="location.address"
  614. :rules="[requiredRule]"
  615. density="compact"
  616. variant="outlined"
  617. placeholder="需在安全且便於學徒到達之地點開課"
  618. ></v-text-field>
  619. <v-btn
  620. @click="getCoordinates"
  621. color="purple"
  622. variant="flat"
  623. class="ms-3 mb-6 px-8"
  624. >
  625. 查詢
  626. </v-btn>
  627. </v-label>
  628. <div class="d-flex flex-column justify-end ms-16 ps-5">
  629. <div
  630. class="map"
  631. id="map"
  632. style="width: 100%; height: 500px"
  633. ></div>
  634. <v-row class="mt-3">
  635. <v-col cols="12" md="6">
  636. <v-label class="d-flex align-center">
  637. <p class="pb-5 pe-3">經度</p>
  638. <v-text-field
  639. v-model="location.Lng"
  640. density="compact"
  641. variant="outlined"
  642. disabled
  643. ></v-text-field>
  644. </v-label>
  645. </v-col>
  646. <v-col cols="12" md="6">
  647. <v-label class="d-flex align-center">
  648. <p class="pb-5 pe-3">緯度</p>
  649. <v-text-field
  650. v-model="location.Lat"
  651. density="compact"
  652. variant="outlined"
  653. disabled
  654. ></v-text-field>
  655. </v-label>
  656. </v-col>
  657. </v-row>
  658. </div>
  659. <v-label class="d-block">
  660. <p class="d-flex mb-5">據點 Email<span class="mark">*</span></p>
  661. <v-text-field
  662. v-model="location.email"
  663. :rules="[requiredRule]"
  664. density="compact"
  665. variant="outlined"
  666. placeholder="請填寫 Email"
  667. ></v-text-field>
  668. </v-label>
  669. <v-row class="mt-3">
  670. <v-col cols="12" md="12">
  671. <v-label class="d-block">
  672. <p class="d-flex">公開電話<span class="mark">*</span></p>
  673. <span class="d-block py-3 hint"
  674. >會顯示於課程介紹中,想了解課程資訊者聯繫用途</span
  675. >
  676. <v-text-field
  677. v-model="location.phone"
  678. :rules="[requiredRule]"
  679. density="compact"
  680. variant="outlined"
  681. ></v-text-field>
  682. </v-label>
  683. </v-col>
  684. <!-- <v-col cols="12" md="6">
  685. <v-label class="d-block">
  686. <p class="d-flex">聯繫電話<span class="mark">*</span></p>
  687. <span class="d-block py-3 hint">平台專員連繫重要事項用</span>
  688. <v-text-field
  689. :rules="[(v) => !!v || '請輸入電話']"
  690. density="compact"
  691. variant="outlined"
  692. ></v-text-field>
  693. </v-label>
  694. </v-col> -->
  695. <!-- <v-col cols="12" md="6">
  696. <v-label class="d-block">
  697. <p class="d-flex">上傳據點照片</p>
  698. <v-file-input
  699. ref="fileInputRef"
  700. label="File input"
  701. variant="outlined"
  702. placeholder="選擇相片上傳"
  703. @change="handlePortfolioImg"
  704. style="display: none"
  705. ></v-file-input>
  706. </v-label>
  707. <v-btn @click="openFileInput" color="purple" class="my-5"
  708. >選擇相片上傳</v-btn
  709. >
  710. <div class="step-01 image-preview">
  711. <img
  712. v-if="selectedFile"
  713. :src="selectedFileUrl"
  714. alt="上傳圖片預覽"
  715. />
  716. </div>
  717. </v-col> -->
  718. <v-col cols="12" md="12">
  719. <v-label class="d-block">
  720. <p class="d-flex mb-5">據點簡介<span class="mark">*</span></p>
  721. <v-textarea
  722. v-model="location.introduction"
  723. rows="5"
  724. variant="outlined"
  725. ></v-textarea>
  726. </v-label>
  727. </v-col>
  728. </v-row>
  729. </v-card-text>
  730. </v-window-item>
  731. <v-window-item :value="2">
  732. <v-card-text>
  733. <v-row>
  734. <v-col cols="6">
  735. <v-label class="d-flex align-center pb-3">
  736. <p class="pb-5 pe-3">老師姓名<span class="mark">*</span></p>
  737. <v-text-field
  738. v-model="resume.teacher_name"
  739. :rules="[requiredRule]"
  740. density="compact"
  741. variant="outlined"
  742. ></v-text-field>
  743. </v-label>
  744. </v-col>
  745. <v-col cols="6"></v-col>
  746. <v-col cols="3">
  747. <v-label class="d-block">
  748. <p class="pb-5 pe-3">工作性質<span class="mark">*</span></p>
  749. <v-radio-group v-model="resume.work_type" inline>
  750. <v-radio label="全職" value="全職"></v-radio>
  751. <v-radio label="兼職" value="兼職" class="ps-3"></v-radio>
  752. </v-radio-group>
  753. </v-label>
  754. </v-col>
  755. <v-col cols="9">
  756. <v-label class="d-block">
  757. <p class="pb-5 pe-3">教學經驗</p>
  758. <v-radio-group v-model="resume.experience" inline>
  759. <v-radio label="0-5 年" value="0-5 年"></v-radio>
  760. <v-radio
  761. label="5-10 年"
  762. value="5-10 年"
  763. class="ps-3"
  764. ></v-radio>
  765. <v-radio
  766. label="10-20 年"
  767. value="10-20 年"
  768. class="ps-3"
  769. ></v-radio>
  770. <v-radio
  771. label="20 年以上"
  772. value="20 年以上"
  773. class="ps-3"
  774. ></v-radio>
  775. </v-radio-group>
  776. </v-label>
  777. </v-col>
  778. <v-col cols="6">
  779. <v-label class="d-block pb-3">
  780. <p class="pb-5 pe-3">
  781. 專長工藝技能<span class="mark">*</span>
  782. </p>
  783. <v-text-field
  784. v-model="resume.expertise"
  785. :rules="[requiredRule]"
  786. density="compact"
  787. variant="outlined"
  788. ></v-text-field>
  789. </v-label>
  790. <v-label class="d-block pb-3">
  791. <p class="pb-5 pe-3">工藝相關證照</p>
  792. <v-text-field
  793. v-model="resume.license"
  794. density="compact"
  795. variant="outlined"
  796. ></v-text-field>
  797. </v-label>
  798. <v-label class="d-block">
  799. <p class="d-flex">社群媒體</p>
  800. <span class="d-block py-3 hint"
  801. >工藝老師經營之網站或社群媒體,可貼網址</span
  802. >
  803. <v-text-field
  804. v-model="resume.media"
  805. density="compact"
  806. variant="outlined"
  807. ></v-text-field>
  808. </v-label>
  809. </v-col>
  810. <v-col cols="6">
  811. <v-label class="d-block">
  812. <p class="d-flex">講師作品集</p>
  813. <v-file-input
  814. multiple
  815. v-model="portfolioImg"
  816. ref="portfolioImgRef"
  817. label="File input"
  818. variant="outlined"
  819. placeholder="選擇相片上傳"
  820. @change="handlePortfolioImg"
  821. style="display: none"
  822. ></v-file-input>
  823. </v-label>
  824. <v-btn
  825. @click="fileInputClick('portfolio')"
  826. color="purple"
  827. class="my-5"
  828. >選擇相片上傳</v-btn
  829. >
  830. <v-row class="img-list">
  831. <v-col cols="4" v-for="(item, index) in 6" :key="index">
  832. <div v-if="!portfolioImgList.length" class="item"></div>
  833. <div
  834. v-else
  835. class="item"
  836. :style="{
  837. backgroundImage: `url('${portfolioImgList[index]}')`,
  838. }"
  839. ></div>
  840. </v-col>
  841. </v-row>
  842. <!-- <div class="step-02 image-preview">
  843. <img
  844. v-if="selectedFile"
  845. :src="selectedFileUrl"
  846. alt="上傳圖片預覽"
  847. />
  848. </div> -->
  849. </v-col>
  850. <v-col cols="12">
  851. <v-label class="d-block">
  852. <p class="d-flex mb-5">老師自我介紹</p>
  853. <v-textarea
  854. v-model="resume.introduction"
  855. rows="5"
  856. variant="outlined"
  857. ></v-textarea>
  858. </v-label>
  859. </v-col>
  860. </v-row>
  861. </v-card-text>
  862. </v-window-item>
  863. <v-window-item :value="3">
  864. <v-card-text class="mb-7">
  865. <v-row class="justify-space-evenly">
  866. <v-col cols="12" md="5">
  867. <v-label class="d-block pb-3">
  868. <p class="pb-5 pe-3">課程名稱<span class="mark">*</span></p>
  869. <v-text-field
  870. v-model="course.name"
  871. :rules="[requiredRule]"
  872. density="compact"
  873. variant="outlined"
  874. ></v-text-field>
  875. </v-label>
  876. </v-col>
  877. <!-- <v-col cols="12" md="5">
  878. <v-label class="d-block pb-3">
  879. <p class="pb-5 pe-3">課程價格<span class="mark">*</span></p>
  880. <v-text-field
  881. :rules="[requiredRule]"
  882. density="compact"
  883. variant="outlined"
  884. ></v-text-field>
  885. </v-label>
  886. </v-col> -->
  887. <v-col cols="12" md="5">
  888. <v-label class="d-block pb-3">
  889. <p class="pb-5 pe-3">工藝類別<span class="mark">*</span></p>
  890. <v-select
  891. v-model="course.category"
  892. placeholder="請選擇類別"
  893. :items="[
  894. '樹藝',
  895. '漆藝',
  896. '藍染',
  897. '蠟雕',
  898. '竹工藝籃',
  899. '金工/飾品',
  900. '蠟燭/香氛/調香',
  901. '植栽/花藝',
  902. '插畫/繪畫/寫字',
  903. '皮件/皮革',
  904. '木工/竹藝',
  905. '陶藝/玻璃',
  906. '編織/羊毛氈/縫紉',
  907. '其他',
  908. ]"
  909. :rules="[requiredRule]"
  910. hide-details
  911. density="compact"
  912. variant="outlined"
  913. ></v-select>
  914. </v-label>
  915. </v-col>
  916. <!-- <v-col cols="12" md="5">
  917. <v-label class="d-block pb-3">
  918. <p class="pb-5 pe-3">
  919. 適合報名對象(年紀)<span class="mark">*</span>
  920. </p>
  921. <v-select
  922. v-model="age"
  923. :items="[
  924. '不拘',
  925. '7 歲以下',
  926. '7-18 歲',
  927. '18-65 歲',
  928. '65 歲以上',
  929. ]"
  930. :rules="[requiredRule]"
  931. density="compact"
  932. variant="outlined"
  933. outlined
  934. ></v-select>
  935. </v-label>
  936. </v-col> -->
  937. <v-col cols="12" md="5">
  938. <v-label class="d-block pb-3">
  939. <p class="pb-5 pe-3">上課地點</p>
  940. <v-text-field
  941. v-model="location.address"
  942. label="自動帶入據點地址"
  943. density="compact"
  944. variant="outlined"
  945. ></v-text-field>
  946. </v-label>
  947. </v-col>
  948. <v-col cols="12" md="5">
  949. <v-label class="d-block pb-3">
  950. <p class="pb-5 pe-3">主辦單位</p>
  951. <v-text-field
  952. v-model="location.location_name"
  953. label="自動帶入據點名稱"
  954. density="compact"
  955. variant="outlined"
  956. ></v-text-field>
  957. </v-label>
  958. </v-col>
  959. <v-col cols="12" md="11" class="px-7">
  960. <v-label class="d-block">
  961. <p class="d-flex">課程時間<span class="mark">*</span></p>
  962. <!-- <v-btn color="purple" class="my-5"
  963. >
  964. <v-icon icon="mdi-plus" class="me-1"></v-icon>
  965. 建立新場次</v-btn
  966. > -->
  967. <v-dialog v-model="sessionsDialog" persistent width="800">
  968. <template v-slot:activator="{ props }">
  969. <v-btn class="my-5" color="purple" v-bind="props">
  970. <v-icon icon="mdi-plus" class="me-1"></v-icon>
  971. 建立新場次
  972. </v-btn>
  973. </template>
  974. <v-card class="sessions-card pb-3">
  975. <v-card-title>
  976. <span class="d-block ps-10 py-5">建立新場次</span>
  977. </v-card-title>
  978. <v-card-text class="pt-0 px-8">
  979. <v-container class="py-0">
  980. <v-row>
  981. <v-col cols="12" sm="6" class="date-item">
  982. <p class="mb-0 pe-3">
  983. 起始日期<span class="mark">*</span>
  984. </p>
  985. <VueDatePicker
  986. v-model="date.start_date"
  987. :min-date="new Date()"
  988. :enable-time-picker="false"
  989. :format="store.datePickerFormat"
  990. locale="cn"
  991. ></VueDatePicker>
  992. </v-col>
  993. <v-col cols="12" sm="6" class="date-item">
  994. <p class="mb-0 pe-3">
  995. 起始時間<span class="mark">*</span>
  996. </p>
  997. <VueDatePicker
  998. v-model="date.start_time"
  999. time-picker
  1000. />
  1001. </v-col>
  1002. <v-col cols="12" sm="6" class="date-item">
  1003. <p class="mb-0 pe-3">
  1004. 結束日期<span class="mark">*</span>
  1005. </p>
  1006. <VueDatePicker
  1007. v-model="date.end_date"
  1008. :min-date="new Date()"
  1009. :enable-time-picker="false"
  1010. :format="store.datePickerFormat"
  1011. locale="cn"
  1012. ></VueDatePicker>
  1013. </v-col>
  1014. <v-col cols="12" sm="6" class="date-item">
  1015. <p class="mb-0 pe-3">
  1016. 結束時間<span class="mark">*</span>
  1017. </p>
  1018. <VueDatePicker
  1019. v-model="date.end_time"
  1020. time-picker
  1021. />
  1022. </v-col>
  1023. <v-divider class="mt-5 mb-8"></v-divider>
  1024. <v-col cols="12" sm="6" class="py-0">
  1025. <v-label class="d-flex align-center py-2">
  1026. <p class="pb-5 pe-3">
  1027. 場次名稱<span class="mark">*</span>
  1028. </p>
  1029. <v-text-field
  1030. v-model="event.event"
  1031. :rules="[requiredRule]"
  1032. density="compact"
  1033. variant="outlined"
  1034. ></v-text-field>
  1035. </v-label>
  1036. </v-col>
  1037. <v-col cols="12" sm="6" class="py-0">
  1038. <v-label class="d-flex align-center py-2">
  1039. <p class="pb-5 pe-3">
  1040. 課程講師<span class="mark">*</span>
  1041. </p>
  1042. <v-text-field
  1043. v-model="event.lecturer"
  1044. :rules="[requiredRule]"
  1045. density="compact"
  1046. variant="outlined"
  1047. ></v-text-field>
  1048. </v-label>
  1049. </v-col>
  1050. <v-col cols="12" sm="6" class="py-0">
  1051. <v-label class="d-flex align-center py-2">
  1052. <p class="pb-5 pe-3">
  1053. 聯絡資訊<span class="mark">*</span>
  1054. </p>
  1055. <v-text-field
  1056. v-model="event.contact"
  1057. :rules="[requiredRule]"
  1058. density="compact"
  1059. variant="outlined"
  1060. ></v-text-field>
  1061. </v-label>
  1062. </v-col>
  1063. <v-col cols="12" sm="6" class="py-0">
  1064. <v-label class="d-flex align-center py-2">
  1065. <p class="pb-5 pe-3">
  1066. 課程名額<span class="mark">*</span>
  1067. </p>
  1068. <v-text-field
  1069. v-model="event.number_limit"
  1070. :rules="[requiredRule]"
  1071. density="compact"
  1072. variant="outlined"
  1073. type="number"
  1074. hint="請設定報名人數上限"
  1075. ></v-text-field>
  1076. </v-label>
  1077. </v-col>
  1078. <v-col cols="12" class="py-0">
  1079. <v-label class="d-flex align-center py-2">
  1080. <p class="pb-5 pe-3">
  1081. 收費方式<span class="mark">*</span>
  1082. </p>
  1083. <v-select
  1084. v-model="event.fee_payment"
  1085. :items="['現場收費', '匯款']"
  1086. :rules="[requiredRule]"
  1087. density="compact"
  1088. variant="outlined"
  1089. outlined
  1090. ></v-select>
  1091. </v-label>
  1092. </v-col>
  1093. <v-col cols="12" class="py-0">
  1094. <v-label
  1095. v-if="isRemit"
  1096. class="d-flex align-center py-2"
  1097. >
  1098. <p class="pb-5 pe-3">
  1099. 匯款資訊<span class="mark">*</span>
  1100. </p>
  1101. <div class="d-flex w-100">
  1102. <v-text-field
  1103. v-model="bankCode"
  1104. label="銀行代碼"
  1105. :rules="[requiredRule]"
  1106. density="compact"
  1107. variant="outlined"
  1108. style="width: 30%"
  1109. class="me-3"
  1110. ></v-text-field>
  1111. <v-text-field
  1112. v-model="bankAccount"
  1113. label="匯款帳號 (10-16 碼)"
  1114. :rules="[requiredRule]"
  1115. density="compact"
  1116. variant="outlined"
  1117. style="width: 70%"
  1118. ></v-text-field>
  1119. </div>
  1120. </v-label>
  1121. </v-col>
  1122. <v-col cols="12" sm="6" class="py-0">
  1123. <v-label class="d-flex align-center py-2">
  1124. <p class="pb-5 pe-3">
  1125. 課程價格<span class="mark">*</span>
  1126. </p>
  1127. <v-text-field
  1128. v-model="event.fee_method"
  1129. :rules="[requiredRule]"
  1130. density="compact"
  1131. variant="outlined"
  1132. ></v-text-field>
  1133. </v-label>
  1134. </v-col>
  1135. <v-col cols="12" sm="6" class="py-0">
  1136. <v-label class="d-flex align-center">
  1137. <p class="pb-3 pe-5">
  1138. 適合報名<br />對象<span class="mark pb-4"
  1139. >*</span
  1140. >
  1141. </p>
  1142. <v-select
  1143. v-model="event.people"
  1144. :items="[
  1145. '不拘',
  1146. '7 歲以下',
  1147. '7-18 歲',
  1148. '18-65 歲',
  1149. '65 歲以上',
  1150. ]"
  1151. :rules="[requiredRule]"
  1152. density="compact"
  1153. variant="outlined"
  1154. outlined
  1155. ></v-select>
  1156. <!-- <v-text-field
  1157. :rules="[requiredRule]"
  1158. density="compact"
  1159. variant="outlined"
  1160. ></v-text-field> -->
  1161. </v-label>
  1162. </v-col>
  1163. <v-col cols="12" class="py-0 position-relative">
  1164. <v-label class="d-flex align-center py-2">
  1165. <p class="pb-3 pe-3">
  1166. 課程類型<span class="mark pb-4">*</span>
  1167. </p>
  1168. <v-select
  1169. v-model="eventType"
  1170. :items="[
  1171. '一日課程(例:2023/10/2 週一上課)',
  1172. '週期課程(例:2023/10/1~2023/10/30 每週一三上課)',
  1173. ]"
  1174. :rules="[requiredRule]"
  1175. density="compact"
  1176. variant="outlined"
  1177. outlined
  1178. ></v-select>
  1179. </v-label>
  1180. <!-- <small
  1181. v-show="eventType !== ''"
  1182. class="type-hint"
  1183. >{{
  1184. isOneDay
  1185. ? "例:2023/10/2 週一上課"
  1186. : "例:2023/10/1~2023/10/30 每週一三上課"
  1187. }}</small
  1188. > -->
  1189. </v-col>
  1190. <v-col
  1191. v-if="!isOneDay"
  1192. cols="12"
  1193. class="d-flex py-0"
  1194. >
  1195. <p class="pt-1 pe-3">
  1196. 重複週期<span class="mark pb-4">*</span>
  1197. </p>
  1198. <ul class="week-list">
  1199. <li
  1200. v-for="(item, index) in weekList"
  1201. :key="index"
  1202. @click="toggleSelected(index)"
  1203. :class="{ active: selectedWeek[index] }"
  1204. class="d-flex"
  1205. >
  1206. <button>
  1207. {{ item }}
  1208. </button>
  1209. <div
  1210. v-if="selectedWeek[index]"
  1211. class="d-flex w-100 ms-5"
  1212. >
  1213. <VueDatePicker
  1214. v-model="
  1215. sessionTime.start_week_time[index]
  1216. "
  1217. time-picker
  1218. />
  1219. <span class="d-flex align-center mx-2"
  1220. >~</span
  1221. >
  1222. <VueDatePicker
  1223. v-model="sessionTime.end_week_time[index]"
  1224. time-picker
  1225. />
  1226. </div>
  1227. </li>
  1228. </ul>
  1229. </v-col>
  1230. <!-- <v-col
  1231. v-if="!isOneDay"
  1232. cols="12"
  1233. class="time-item pt-5 pb-7"
  1234. >
  1235. <p class="mb-0">
  1236. 課程時間<span class="mark">*</span>
  1237. </p>
  1238. <div class="d-flex w-100">
  1239. <VueDatePicker
  1240. v-model="sessionTime.start_week_time"
  1241. time-picker
  1242. />
  1243. <span class="d-flex align-center mx-2">~</span>
  1244. <VueDatePicker
  1245. v-model="sessionTime.end_week_time"
  1246. time-picker
  1247. />
  1248. </div>
  1249. </v-col> -->
  1250. <v-col cols="12" class="py-0">
  1251. <v-label class="d-flex align-center py-2">
  1252. <p class="pe-3">備註</p>
  1253. <v-text-field
  1254. v-model="event.remark"
  1255. density="compact"
  1256. variant="outlined"
  1257. hide-details
  1258. ></v-text-field>
  1259. </v-label>
  1260. </v-col>
  1261. <v-divider class="my-8"></v-divider>
  1262. <v-col cols="12" sm="6" class="date-item">
  1263. <p class="mb-0 pe-3">
  1264. 報名日期<span class="mark pb-4">*</span>
  1265. </p>
  1266. <VueDatePicker
  1267. v-model="date.registration_start_date"
  1268. :min-date="new Date()"
  1269. :enable-time-picker="false"
  1270. :format="store.datePickerFormat"
  1271. locale="cn"
  1272. ></VueDatePicker>
  1273. </v-col>
  1274. <v-col cols="12" sm="6" class="date-item">
  1275. <p class="mb-0 pe-3">
  1276. 報名時間<span class="mark pb-4">*</span>
  1277. </p>
  1278. <VueDatePicker
  1279. v-model="date.registration_start_time"
  1280. time-picker
  1281. />
  1282. </v-col>
  1283. <v-col cols="12" sm="6" class="date-item">
  1284. <p class="mb-0 pe-3">
  1285. 報名截止<span class="mark pb-4">*</span>
  1286. </p>
  1287. <VueDatePicker
  1288. v-model="date.registration_end_date"
  1289. :min-date="new Date()"
  1290. :enable-time-picker="false"
  1291. :format="store.datePickerFormat"
  1292. locale="cn"
  1293. ></VueDatePicker>
  1294. </v-col>
  1295. <v-col cols="12" sm="6" class="date-item">
  1296. <p class="mb-0 pe-3">
  1297. 截止時間<span class="mark pb-4">*</span>
  1298. </p>
  1299. <VueDatePicker
  1300. v-model="date.registration_end_time"
  1301. time-picker
  1302. />
  1303. </v-col>
  1304. </v-row>
  1305. </v-container>
  1306. </v-card-text>
  1307. <v-card-actions class="justify-center pt-10 pb-5">
  1308. <v-btn
  1309. color="gray"
  1310. variant="tonal"
  1311. class="me-3 px-10"
  1312. @click="sessionsDialog = false"
  1313. >
  1314. 取消
  1315. </v-btn>
  1316. <v-btn
  1317. color="purple"
  1318. variant="flat"
  1319. @click="addEventData()"
  1320. class="px-10"
  1321. >
  1322. 新增
  1323. </v-btn>
  1324. </v-card-actions>
  1325. </v-card>
  1326. </v-dialog>
  1327. <div v-if="eventData.list.length" class="main-table">
  1328. <h6 class="table-title">場次資訊</h6>
  1329. <table>
  1330. <thead>
  1331. <tr>
  1332. <th>名稱</th>
  1333. <th>日期</th>
  1334. <th width="20%">課程講師</th>
  1335. <th width="20%">收費方式</th>
  1336. </tr>
  1337. </thead>
  1338. <tbody>
  1339. <tr
  1340. v-for="(item, index) in eventData.list"
  1341. :key="index"
  1342. >
  1343. <td>{{ item.event }}</td>
  1344. <td>
  1345. {{
  1346. moment(`${item.start_time}`).format("YYYY/MM/DD")
  1347. }}
  1348. <br />
  1349. <br />
  1350. {{
  1351. moment(`${item.end_time}`).format("YYYY/MM/DD")
  1352. }}
  1353. </td>
  1354. <td>{{ item.lecturer }}</td>
  1355. <td>{{ item.fee_method }}</td>
  1356. </tr>
  1357. </tbody>
  1358. </table>
  1359. </div>
  1360. </v-label>
  1361. </v-col>
  1362. <v-col cols="12" md="11" class="px-7">
  1363. <v-label class="d-block">
  1364. <p class="d-flex">課程簡介<span class="mark">*</span></p>
  1365. <small class="d-block text-gray my-2"
  1366. >內容不得少於 100 字元,且不得含有工藝無關之資訊</small
  1367. >
  1368. <v-textarea
  1369. v-model="course.introduction"
  1370. rows="5"
  1371. variant="outlined"
  1372. ></v-textarea>
  1373. </v-label>
  1374. </v-col>
  1375. <v-col cols="12" md="6" class="me-auto ms-3 ps-16">
  1376. <v-label class="d-block">
  1377. <p class="d-flex">上傳課程封面<span class="mark">*</span></p>
  1378. <v-file-input
  1379. multiple
  1380. v-model="coverImg"
  1381. ref="coverImgRef"
  1382. label="File input"
  1383. variant="outlined"
  1384. placeholder="選擇相片上傳"
  1385. @change="handleCoverImg"
  1386. style="display: none"
  1387. ></v-file-input>
  1388. </v-label>
  1389. <v-btn
  1390. @click="fileInputClick('cover')"
  1391. color="purple"
  1392. class="my-5"
  1393. >選擇相片上傳</v-btn
  1394. >
  1395. <div class="step-01 image-preview">
  1396. <img v-if="coverImg" :src="coverImgUrl" alt="上傳圖片預覽" />
  1397. </div>
  1398. <!-- <v-row class="img-list">
  1399. <v-col cols="4" v-for="(item, index) in 6" :key="index">
  1400. <div v-if="!portfolioImgList.length" class="item"></div>
  1401. <div
  1402. v-else
  1403. class="item"
  1404. :style="{
  1405. backgroundImage: `url('${portfolioImgList[index]}')`,
  1406. }"
  1407. ></div>
  1408. </v-col>
  1409. </v-row> -->
  1410. </v-col>
  1411. </v-row>
  1412. </v-card-text>
  1413. </v-window-item>
  1414. <v-window-item :value="4">
  1415. <v-card-text class="mb-7 finish-step">
  1416. <p>
  1417. 請等待後台人員確認您的課程資訊 <br />
  1418. 待您收到開課完成的 Email <br />
  1419. 就可以前往
  1420. <router-link to="/user/courses">【我的開課】</router-link>
  1421. 觀看及修改您的
  1422. </p>
  1423. <ul>
  1424. <li>(1) 據點資訊</li>
  1425. <li>(2) 工藝家履歷</li>
  1426. <li>(3) 課程清單</li>
  1427. </ul>
  1428. </v-card-text>
  1429. </v-window-item>
  1430. </v-window>
  1431. <v-divider></v-divider>
  1432. <v-card-actions class="justify-center mt-7">
  1433. <v-btn
  1434. v-if="step > 1 && step !== 4"
  1435. color="gray"
  1436. variant="outlined"
  1437. @click="step--"
  1438. class="px-7 me-2"
  1439. >
  1440. 上一步
  1441. </v-btn>
  1442. <v-spacer></v-spacer>
  1443. <v-btn
  1444. v-if="step < 3"
  1445. color="purple"
  1446. variant="flat"
  1447. @click="step++"
  1448. class="px-7"
  1449. >
  1450. 下一步
  1451. </v-btn>
  1452. <v-btn
  1453. v-if="step === 3"
  1454. color="purple"
  1455. variant="flat"
  1456. @click="create()"
  1457. :loading="loading"
  1458. class="px-7"
  1459. >
  1460. 創建
  1461. </v-btn>
  1462. <v-btn v-if="step === 4" color="purple" variant="outlined" class="me-3">
  1463. <router-link to="/" class="px-7">回到首頁</router-link>
  1464. </v-btn>
  1465. <v-btn v-if="step === 4" color="purple" variant="flat">
  1466. <router-link to="/user/courses" class="px-7"
  1467. >前往開課專區</router-link
  1468. >
  1469. </v-btn>
  1470. </v-card-actions>
  1471. </v-card>
  1472. </v-container>
  1473. </template>
  1474. <style lang="scss" scoped>
  1475. .step-title {
  1476. text-align: center;
  1477. h5 {
  1478. font-size: 28px;
  1479. font-weight: 500;
  1480. letter-spacing: 2px;
  1481. }
  1482. p {
  1483. font-size: 16px;
  1484. font-weight: 400;
  1485. letter-spacing: 1px;
  1486. color: #919191;
  1487. }
  1488. }
  1489. .step-01 {
  1490. &.image-preview {
  1491. height: 285px;
  1492. }
  1493. }
  1494. .step-02 {
  1495. &.image-preview {
  1496. height: 235px;
  1497. }
  1498. }
  1499. .image-preview {
  1500. img {
  1501. height: 100%;
  1502. object-fit: contain;
  1503. }
  1504. }
  1505. .flex-grow-1 {
  1506. display: none !important;
  1507. }
  1508. .img-list {
  1509. .item {
  1510. height: 105px;
  1511. border-radius: 5px;
  1512. background-color: #ccc; // 預設灰底
  1513. background-repeat: no-repeat;
  1514. background-position: center;
  1515. background-size: cover;
  1516. }
  1517. }
  1518. .sessions-card {
  1519. border-radius: 30px 5px 5px 30px !important;
  1520. .v-label {
  1521. p {
  1522. width: 95px;
  1523. text-align: end;
  1524. line-height: 22px;
  1525. @media (max-width: 600px) {
  1526. width: 123px;
  1527. }
  1528. }
  1529. }
  1530. }
  1531. .date-item {
  1532. display: flex;
  1533. margin-bottom: 10px;
  1534. p {
  1535. width: 114px;
  1536. display: flex;
  1537. align-items: center;
  1538. justify-content: end;
  1539. white-space: nowrap;
  1540. line-height: 22px;
  1541. @media (max-width: 600px) {
  1542. width: 125px;
  1543. }
  1544. }
  1545. }
  1546. .main-table {
  1547. margin: 50px 0;
  1548. .table-title {
  1549. background-color: var(--purple);
  1550. }
  1551. table {
  1552. thead {
  1553. border-bottom: 2px solid var(--purple);
  1554. }
  1555. tbody {
  1556. td {
  1557. border-bottom: 1px solid var(--purple);
  1558. }
  1559. }
  1560. }
  1561. }
  1562. .finish-step {
  1563. line-height: 50px;
  1564. font-size: 22px;
  1565. text-align: center;
  1566. letter-spacing: 1px;
  1567. }
  1568. .type-hint {
  1569. width: 100%;
  1570. display: block;
  1571. position: absolute;
  1572. bottom: 1px;
  1573. left: 105px;
  1574. color: var(--gray);
  1575. }
  1576. .week-list {
  1577. height: 70%;
  1578. align-items: center;
  1579. li {
  1580. margin: 15px 0;
  1581. button {
  1582. color: var(--purple);
  1583. padding: 10px;
  1584. border-radius: 100px;
  1585. border: 1px solid var(--purple);
  1586. transition: all 0.3s;
  1587. &:hover {
  1588. color: #fff;
  1589. background-color: var(--purple);
  1590. }
  1591. }
  1592. &.active {
  1593. button {
  1594. color: #fff;
  1595. background-color: var(--purple);
  1596. }
  1597. }
  1598. }
  1599. }
  1600. .time-item {
  1601. display: flex;
  1602. align-items: center;
  1603. p {
  1604. width: 97px;
  1605. flex-wrap: nowrap;
  1606. display: flex;
  1607. white-space: nowrap;
  1608. }
  1609. }
  1610. .school-list {
  1611. width: 350px;
  1612. margin: auto;
  1613. }
  1614. </style>