Create.vue 95 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747
  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. const breadcrumbs = reactive([
  51. {
  52. title: "首頁",
  53. disabled: false,
  54. href: "/",
  55. },
  56. {
  57. title: "我要開課",
  58. disabled: false,
  59. href: "/setup-courses",
  60. },
  61. {
  62. title: "創建課程",
  63. disabled: true,
  64. },
  65. ]);
  66. // // Google Map
  67. // const states = reactive({
  68. // google: null,
  69. // map: null,
  70. // markers: null,
  71. // });
  72. // const initMap = async () => {
  73. // const loader = new Loader({
  74. // apiKey: "AIzaSyClxiB7zcQDyGaB1r3Ww_VQG950AtjVoAk",
  75. // version: "weekly",
  76. // libraries: ["places"],
  77. // language: "zh-TW",
  78. // });
  79. // states.google = await loader.load();
  80. // states.map = new states.google.maps.Map(document.getElementById("map"), {
  81. // center: { lat: 25.0425, lng: 121.5468 },
  82. // zoom: 11,
  83. // mapTypeControl: false,
  84. // fullscreenControl: false,
  85. // });
  86. // };
  87. // onMounted(async () => {
  88. // await initMap();
  89. // });
  90. let location = reactive({
  91. location_name: "",
  92. Lng: "",
  93. Lat: "",
  94. address: "",
  95. school_introduction: "",
  96. email: "",
  97. phone: "",
  98. access_token: token,
  99. });
  100. let locationId = ref("");
  101. // async function insertSchool() {
  102. // // 若已選擇據點則不需新增
  103. // if (schoolId.value !== "") {
  104. // locationId.value = schoolId.value;
  105. // return;
  106. // }
  107. // const formData = new FormData();
  108. // for (const key in location) {
  109. // formData.append(key, location[key]);
  110. // }
  111. // try {
  112. // const response = await axios.post(
  113. // "https://cmm.ai:8088/api/insert_school",
  114. // formData
  115. // );
  116. // locationId.value = response.data.location_id;
  117. // console.log("新增據點 response", response);
  118. // } catch (error) {
  119. // console.error(error);
  120. // }
  121. // }
  122. let resume = reactive({
  123. teacher_name: "", // 老師姓名
  124. work_type: "", // 工作性質
  125. experience: "", // 教學經驗
  126. expertise: "", // 專長工藝技能
  127. license: "", // 工藝相關證照
  128. media: "", // 社群媒體
  129. files: [], // 作品集
  130. introduction: "", // 老師介紹
  131. });
  132. let resumeList = ref([]);
  133. let resumeDialog = ref(false);
  134. // 新增工藝教育者履歷
  135. async function insertResume() {
  136. const validationResult = await resumeForm.value.validate();
  137. // 驗證表單
  138. if (validationResult.valid) {
  139. resumeList.value.push(JSON.parse(JSON.stringify(resume)));
  140. alert("新增成功!");
  141. resumeDialog.value = false;
  142. // 清空表單的值
  143. for (const key in resume) {
  144. if (resume.hasOwnProperty(key)) {
  145. resume[key] = "";
  146. }
  147. }
  148. } else {
  149. return;
  150. }
  151. }
  152. // let oldFile = "";
  153. // async function insertUserResume() {
  154. // if (portfolioImg.value.length) {
  155. // resume.files = portfolioImg.value;
  156. // }
  157. // const formData = new FormData();
  158. // for (const key in resume) {
  159. // if (key === "files") {
  160. // resume.files.forEach((file) => {
  161. // formData.append("files", file);
  162. // });
  163. // } else {
  164. // formData.append(key, resume[key]);
  165. // }
  166. // }
  167. // let token = store.token;
  168. // try {
  169. // const response = await axios.post(
  170. // `https://cmm.ai:8088/api/input_user_resume?access_token=${token}&old_file=${oldFile}`,
  171. // formData
  172. // );
  173. // console.log("新增履歷 response", response);
  174. // } catch (error) {
  175. // console.error(error);
  176. // }
  177. // }
  178. // 新增課程
  179. let course = reactive({
  180. name: "", // 課程名稱
  181. location_id: "", // 據點編號
  182. category: "", // 工藝類別
  183. introduction: "", // 課程簡介
  184. syllabus: "", // 課程章節(課綱)
  185. organizer: "", // 主辦單位
  186. cover_img_file: "", // 課程圖片
  187. group_id: 2, // 學群編號(技藝)
  188. group_sort: "", // 學群細分
  189. special_class_list_name: "",
  190. recommend: 0, // 是否推薦
  191. is_inner: 1, // 內課課程
  192. is_check: 0, // 審核結果
  193. teachers_resume: "", // 指派工藝教育者
  194. access_token: token,
  195. });
  196. // let syllabusData = reactive({
  197. // title: "",
  198. // content: "",
  199. // });
  200. let section = ref("");
  201. let sectionList = ref([]);
  202. function addSection() {
  203. sectionList.value.push(section.value);
  204. section.value = "";
  205. }
  206. watch(location, (data) => {
  207. course.organizer = data.location_name; // 據點名稱 = 主辦單位
  208. });
  209. let classNameId = ref("");
  210. async function insertClassName() {
  211. course.location_id = locationId.value;
  212. course.organizer = locationData.value.location_name;
  213. course.teachers_resume = JSON.stringify(assignTeachers.list);
  214. course.syllabus = JSON.stringify(sectionList.value);
  215. console.log("insertClassName", course);
  216. if (coverImg.value !== "") {
  217. course.cover_img_file = coverImg.value;
  218. }
  219. const formData = new FormData();
  220. for (const key in course) {
  221. formData.append(key, course[key]);
  222. }
  223. try {
  224. const response = await axios.post(
  225. "https://cmm.ai:8088/api/insert_class_name",
  226. formData
  227. );
  228. console.log("新增課程 response", response);
  229. classNameId.value = response.data.new_class_name_id;
  230. } catch (error) {
  231. console.error(error);
  232. }
  233. }
  234. // 新增場次
  235. let event = reactive({
  236. name_id: "", // 課程名稱編號(需先新增課程取得Id)
  237. event: "", // 場次名稱
  238. start_time: "", // 課程起始日
  239. end_time: "", // 課程結束日
  240. contact: "", // 聯絡資訊
  241. lecturer: "", // 課程講師
  242. location: "", // 教室名稱
  243. content: "", // 課程內容
  244. URL: "", // 外部連結
  245. people: "不拘", // 對象
  246. fee_method: "", // 課程價格
  247. fee_payment: "", // 收費方式
  248. registration_way: "", // 報名方式
  249. registration_start: "", // 報名起始日
  250. registration_end: "", // 報名截止日
  251. number_limit: "", // 課程名額
  252. remark: "", // 備註
  253. ATM_address: "", // 匯款帳號(銀行代碼+帳號)
  254. access_token: token,
  255. });
  256. let eventType = ref(""); // 課程類型
  257. let isOneDay = ref(true); // 是否為一日課程
  258. watch(eventType, (val) => {
  259. if (val === "週期課程(例:2023/10/1~2023/10/30 每週一三上課)") {
  260. isOneDay.value = false;
  261. } else {
  262. isOneDay.value = true;
  263. }
  264. });
  265. // 若為一日課程則起始日期 = 結束日期
  266. watch(date, (val) => {
  267. if (isOneDay.value && val.start_date) {
  268. date.end_date = val.start_date;
  269. }
  270. });
  271. let dateReminder = ref(false);
  272. let eventData = reactive({
  273. list: [],
  274. });
  275. let isRemit = ref(false); // 是否顯示匯款資訊欄位
  276. let bankCode = ref(""); // 銀行代碼
  277. let bankAccount = ref(""); // 銀行帳號
  278. watch(event, (data) => {
  279. if (data.fee_payment === "匯款") {
  280. isRemit.value = true;
  281. } else {
  282. isRemit.value = false;
  283. }
  284. });
  285. let eventFieldValidate = ref(false); // 驗證場次欄位
  286. function addEventData() {
  287. // insertSession(); // 測試
  288. console.log("event", event);
  289. let isSame = store.isSameDay(date.start_date, date.end_date);
  290. // 起始日期不可等於結束日期(週期課程)
  291. if (!isOneDay.value && isSame) {
  292. dateReminder.value = true;
  293. return;
  294. }
  295. dateReminder.value = false;
  296. eventForm.value.validate();
  297. // 檢查日期欄位
  298. for (const key in date) {
  299. if (!date[key]) {
  300. return;
  301. }
  302. }
  303. // 檢查必填欄位
  304. if (
  305. event.event === "" || // 場次名稱
  306. event.contact === "" || // 聯絡資訊
  307. event.lecturer === "" || // 課程講師
  308. event.people === "" || // 對象
  309. event.fee_method === "" || // 課程價格
  310. event.fee_payment === "" || // 收費方式
  311. event.number_limit === "" || // 課程名額
  312. eventType.value === "" // 課程類型
  313. ) {
  314. return;
  315. } else {
  316. eventFieldValidate.value = true;
  317. }
  318. // event.location = location.address;
  319. // console.log("location.address", location.address);
  320. console.log("event.location", event.location);
  321. // 處理時間格式
  322. event.start_time = store.mergeAndFormatDateTime(
  323. date.start_date,
  324. date.start_time
  325. );
  326. event.end_time = store.mergeAndFormatDateTime(date.end_date, date.end_time);
  327. event.registration_start = store.mergeAndFormatDateTime(
  328. date.registration_start_date,
  329. date.registration_start_time
  330. );
  331. event.registration_end = store.mergeAndFormatDateTime(
  332. date.registration_end_date,
  333. date.registration_end_time
  334. );
  335. if (isRemit && bankCode.value !== "" && bankAccount.value !== "") {
  336. event.ATM_address = `(${bankCode.value})-${bankAccount.value}`;
  337. }
  338. eventData.list.push(JSON.parse(JSON.stringify(event)));
  339. sessionsDialog.value = false;
  340. }
  341. function convertDateFormat(inputDateStr) {
  342. const inputDate = new Date(inputDateStr);
  343. const formattedDate = inputDate.toISOString();
  344. return formattedDate;
  345. }
  346. let eventId = ref(null);
  347. async function insertEvent() {
  348. console.log("insertEvent", event);
  349. if (classNameId.value !== "") {
  350. event.name_id = classNameId.value;
  351. }
  352. event.start_time = convertDateFormat(event.start_time);
  353. event.end_time = convertDateFormat(event.end_time);
  354. event.registration_start = convertDateFormat(event.registration_start);
  355. event.registration_end = convertDateFormat(event.registration_end);
  356. console.log("檢查日期格式", event);
  357. const formData = new FormData();
  358. for (const key in event) {
  359. formData.append(key, event[key]);
  360. }
  361. try {
  362. const response = await axios.post(
  363. "https://cmm.ai:8088/api/insert_event",
  364. formData
  365. );
  366. eventId.value = response.data.class_id;
  367. console.log("eventId.value", eventId.value);
  368. console.log("新增場次 response", response);
  369. } catch (error) {
  370. console.error(error);
  371. }
  372. }
  373. let errorEventField = ref(false);
  374. // 創建課程
  375. async function create() {
  376. console.log("course", course);
  377. loading.value = true;
  378. // 檢查必填欄位
  379. courseForm.value.validate();
  380. console.log("course", course);
  381. if (
  382. course.name === "" ||
  383. course.category === "" ||
  384. course.introduction === "" ||
  385. coverImg.value === ""
  386. ) {
  387. loading.value = false;
  388. errorField.value = true;
  389. setTimeout(() => {
  390. errorField.value = false;
  391. }, 3000);
  392. return;
  393. }
  394. // 課程時間(場次)尚未填寫完整
  395. if (!eventFieldValidate.value) {
  396. loading.value = false;
  397. errorEventField.value = true;
  398. setTimeout(() => {
  399. errorEventField.value = false;
  400. }, 3000);
  401. return;
  402. }
  403. locationId.value = assignLocationId.value; // 存據點 ID
  404. try {
  405. // await insertSchool(); // 新增據點
  406. // await insertUserResume(); // 新增履歷
  407. await insertClassName(); // 新增課程
  408. await insertEvent(); // 新增場次
  409. await insertSession(); // 新增課堂
  410. loading.value = false;
  411. step.value++;
  412. } catch (error) {
  413. console.error(error);
  414. loading.value = false;
  415. }
  416. }
  417. const getCoordinates = async () => {
  418. const geocoder = new states.google.maps.Geocoder();
  419. geocoder.geocode({ address: location.address }, (results, status) => {
  420. if (status === states.google.maps.GeocoderStatus.OK) {
  421. location.Lat = results[0].geometry.location.lat();
  422. location.Lng = results[0].geometry.location.lng();
  423. }
  424. // 將地圖中心設為取得的經緯度,並調整縮放等級
  425. // states.map.setCenter({ lat: location.Lat, lng: location.Lng });
  426. // states.map.setZoom(15);
  427. // 設定地址圖標
  428. // const marker = new google.maps.Marker({
  429. // position: { lat: location.Lat, lng: location.Lng },
  430. // map: states.map,
  431. // title: location.address,
  432. // icon: store.getImageUrl("map-icon/icon_house05.png"),
  433. // });
  434. });
  435. };
  436. // 上傳圖片 Input
  437. const portfolioImgRef = ref(null);
  438. const coverImgRef = ref(null);
  439. const fileInputClick = (ref) => {
  440. if (ref === "portfolio") {
  441. portfolioImgRef.value.click();
  442. } else if (ref === "cover") {
  443. coverImgRef.value.click();
  444. }
  445. };
  446. // 作品集圖片
  447. let portfolioImg = ref([]);
  448. let portfolioImgList = ref([]);
  449. const handlePortfolioImg = (files) => {
  450. console.log("files", files);
  451. // portfolioImgList.value = []; // 清空陣列
  452. for (let index = 0; index < files.length; index++) {
  453. const file = files[index];
  454. console.log("file", file);
  455. let url = URL.createObjectURL(file);
  456. portfolioImgList.value.push(url);
  457. console.log("portfolioImgList", portfolioImgList.value);
  458. }
  459. };
  460. watch(portfolioImg, (newFiles) => {
  461. if (newFiles.length > 0) {
  462. handlePortfolioImg(newFiles);
  463. }
  464. });
  465. // 封面圖片
  466. let coverImg = ref("");
  467. let coverImgUrl = ref("");
  468. const handleCoverImg = (event) => {
  469. const file = event.target.files[0];
  470. console.log("選擇檔案", file);
  471. if (file) {
  472. coverImg.value = file;
  473. coverImgUrl.value = URL.createObjectURL(file); // 設定圖片預覽 URL
  474. }
  475. };
  476. let sessionsDialog = ref(false);
  477. const requiredRule = (value) => !!value || "此欄位為必填";
  478. const weekList = reactive(["一", "二", "三", "四", "五", "六", "日"]);
  479. const selectedWeek = ref([0, 0, 0, 0, 0, 0, 0]); // 選擇星期
  480. const toggleSelected = (index) => {
  481. if (selectedWeek.value[index]) {
  482. selectedWeek.value[index] = 0;
  483. } else {
  484. selectedWeek.value[index] = 1;
  485. }
  486. };
  487. let session = reactive({
  488. class_event_id: null,
  489. week_day_str: "",
  490. start_week_time: "",
  491. end_week_time: "",
  492. });
  493. let sessionTime = reactive({
  494. start_week_time: [],
  495. end_week_time: [],
  496. });
  497. let sessionStartTimeList = ref(["", "", "", "", "", "", ""]);
  498. let sessionEndTimeList = ref(["", "", "", "", "", "", ""]);
  499. // 新增課堂
  500. async function insertSession() {
  501. console.log("isOneDay.value", isOneDay.value);
  502. const weekString = `[${selectedWeek.value.join(",")}]`;
  503. session.week_day_str = weekString;
  504. console.log("weekString", weekString);
  505. selectedWeek.value.map((item, index) => {
  506. if (item) {
  507. console.log("index", index);
  508. // 開始時間
  509. sessionStartTimeList.value[index] = `"${
  510. sessionTime.start_week_time[index].hours < 10 ? "0" : ""
  511. }${sessionTime.start_week_time[index].hours}:${
  512. sessionTime.start_week_time[index].minutes === 0
  513. ? "00"
  514. : sessionTime.start_week_time[index].minutes
  515. }:00"`;
  516. // 結束時間
  517. sessionEndTimeList.value[index] = `"${
  518. sessionTime.end_week_time[index].hours
  519. }:${
  520. sessionTime.end_week_time[index].minutes === 0
  521. ? "00"
  522. : sessionTime.end_week_time[index].minutes
  523. }:00"`;
  524. } else {
  525. sessionStartTimeList.value[index] = '""';
  526. sessionEndTimeList.value[index] = '""';
  527. }
  528. });
  529. session.start_week_time = `[${sessionStartTimeList.value}]`;
  530. session.end_week_time = `[${sessionEndTimeList.value}]`;
  531. session.class_event_id = eventId.value;
  532. // const formData = new FormData();
  533. // for (const key in session) {
  534. // formData.append(key, session[key]);
  535. // }
  536. let oneDayData = {
  537. class_event_id: session.class_event_id,
  538. start_time: event.start_time,
  539. end_time: event.end_time,
  540. content: "",
  541. };
  542. console.log("oneDayData", oneDayData);
  543. // const oneDayFormData = new FormData();
  544. // for (const key in oneDayData) {
  545. // formData.append(key, oneDayData[key]);
  546. // }
  547. try {
  548. if (isOneDay.value) {
  549. const oneDayFormData = new FormData();
  550. for (const key in oneDayData) {
  551. oneDayFormData.append(key, oneDayData[key]);
  552. }
  553. // 一日課程
  554. const response = await axios.post(
  555. "https://cmm.ai:8088/api/insert_session",
  556. oneDayFormData
  557. );
  558. console.log("新增課堂(一日) response", response);
  559. } else {
  560. const formData = new FormData();
  561. for (const key in session) {
  562. formData.append(key, session[key]);
  563. }
  564. // 週期課程
  565. const response = await axios.post(
  566. "https://cmm.ai:8088/api/auto_create_session",
  567. formData
  568. );
  569. console.log("新增課堂(週期) response", response);
  570. }
  571. // const response = await axios.post(
  572. // "https://cmm.ai:8088/api/auto_create_session",
  573. // formData
  574. // );
  575. // console.log("新增課堂 response", response);
  576. } catch (error) {
  577. console.error(error);
  578. }
  579. }
  580. let schoolSelect = reactive({
  581. list: [],
  582. });
  583. let schoolId = ref("");
  584. // 取得已有據點
  585. (async () => {
  586. let token = store.token;
  587. try {
  588. const response = await axios.get(
  589. `https://cmm.ai:8088/api/get_school?&access_token=${token}`
  590. );
  591. schoolSelect.list = response.data.schools;
  592. // 調整排序(未審核移到最後)
  593. schoolSelect.list.sort((a, b) => b.is_pass_proposal - a.is_pass_proposal);
  594. console.log("取得使用者據點", schoolSelect.list);
  595. } catch (error) {
  596. console.error(error);
  597. }
  598. })();
  599. // 監聽據點 Select
  600. watch(schoolId, (id) => {
  601. console.log("schoolId", id);
  602. // locationId.value = id;
  603. handleSchoolData(id);
  604. });
  605. let schools = reactive({
  606. list: [],
  607. });
  608. // 帶入據點資料
  609. async function handleSchoolData(id) {
  610. console.log("handleSchoolData", id);
  611. try {
  612. const response = await axios.get(
  613. `https://cmm.ai:8088/api/get_school?location_id=${id}`
  614. );
  615. schools.list = response.data.schools;
  616. console.log("取得已有據點", schools.list);
  617. for (const key in schools.list[0]) {
  618. if (location.hasOwnProperty(key) && schools.list[0][key] !== undefined) {
  619. location[key] = schools.list[0][key];
  620. }
  621. }
  622. } catch (error) {
  623. console.error(error);
  624. }
  625. }
  626. /* 據點 */
  627. let assignLocationId = ref(null);
  628. watch(assignLocationId, (val) => {
  629. console.log("assignLocationId", val);
  630. });
  631. let locationData = ref(null);
  632. // 存選擇的據點 ID
  633. function setLocationId(item) {
  634. console.log("setLocationId", item);
  635. locationData.value = item;
  636. console.log("locationData", locationData.value);
  637. if (item.is_pass_proposal === 1) {
  638. assignLocationId.value = item.location_id;
  639. }
  640. }
  641. const schoolForm = ref(null); // Step1 表單
  642. const resumeForm = ref(null); // Step2 表單
  643. const courseForm = ref(null); // Step3 表單
  644. const eventForm = ref(null); // Step4 表單
  645. let errorField = ref(false);
  646. function checkField(num) {
  647. console.log("checkField", num);
  648. // step.value++;
  649. if (num === 1) {
  650. console.log("assignLocationId", assignLocationId.value);
  651. if (!assignLocationId.value) {
  652. console.log("沒有");
  653. errorField.value = true;
  654. setTimeout(() => {
  655. errorField.value = false;
  656. }, 3000);
  657. return;
  658. } else {
  659. getSchool(assignLocationId.value); // 取得指定據點
  660. }
  661. } else if (num === 2) {
  662. if (!assignTeachers.list.length) {
  663. errorField.value = true;
  664. setTimeout(() => {
  665. errorField.value = false;
  666. }, 3000);
  667. return;
  668. }
  669. }
  670. step.value++;
  671. }
  672. let teachers = reactive({
  673. list: [],
  674. });
  675. // 取得指定據點底下的工藝教育者履歷
  676. async function getSchool(id) {
  677. console.log("getSchool", id);
  678. try {
  679. const response = await axios.get(
  680. `https://cmm.ai:8088/api/get_school?location_id=${id}`
  681. );
  682. console.log("取得據點", response);
  683. resumeList.value = JSON.parse(response.data.schools[0].teachers_list);
  684. console.log("resumeList.value", resumeList.value);
  685. } catch (error) {
  686. console.error(error);
  687. }
  688. }
  689. let teacherCheckbox = ref([]);
  690. let assignTeachers = reactive({
  691. list: [],
  692. });
  693. /* 指派工藝教育者 */
  694. async function assignTeacher(item, index) {
  695. console.log("item", item);
  696. console.log("index", index);
  697. // 檢查陣列中是否已存在相同資料
  698. let isDuplicate = assignTeachers.list.some(function (existingItem) {
  699. return (
  700. existingItem.teacher_name === item.teacher_name &&
  701. existingItem.subject === item.subject
  702. );
  703. });
  704. // 如無重複再新增
  705. if (!isDuplicate) {
  706. assignTeachers.list.push(item);
  707. teacherCheckbox.value[index] = true;
  708. console.log("teacherCheckbox", teacherCheckbox.value);
  709. } else {
  710. console.log("已存在");
  711. assignTeachers.list = assignTeachers.list.filter(
  712. (e) => e.teacher_name !== item.teacher_name
  713. );
  714. teacherCheckbox.value[index] = false;
  715. }
  716. console.log("assignTeachers.list", assignTeachers.list);
  717. }
  718. </script>
  719. <template>
  720. <Navbar />
  721. <v-container class="mb-16 pb-16">
  722. <v-breadcrumbs
  723. :items="breadcrumbs"
  724. divider="/"
  725. class="py-10"
  726. ></v-breadcrumbs>
  727. <v-card class="mx-auto pa-5 pa-sm-10">
  728. <v-card-title class="step-title">
  729. <h5>{{ computedTitle.stepTitle }}</h5>
  730. <p class="mt-5">{{ computedTitle.stepDescription }}</p>
  731. </v-card-title>
  732. <v-window v-model="step">
  733. <v-window-item :value="1">
  734. <v-card-text>
  735. <v-form
  736. ref="schoolForm"
  737. class="school-form"
  738. lazy-validation
  739. @submit.prevent
  740. >
  741. <router-link to="/setup-courses/proposal" class="proposal-link">
  742. <v-icon icon="mdi-plus-circle" class="me-2"></v-icon>
  743. 想建立新據點?請點此前往提案</router-link
  744. >
  745. <ul>
  746. <li v-for="(item, index) in schoolSelect.list" :key="index">
  747. <div
  748. class="d-flex justify-space-between"
  749. @click="setLocationId(item)"
  750. >
  751. <div class="d-flex flex-column">
  752. <span class="d-flex align-center">
  753. <label :for="item.location_id" class="me-3">{{
  754. item.location_name
  755. }}</label>
  756. <v-chip
  757. v-if="item.is_pass_proposal === 1"
  758. color="green"
  759. text-color="white"
  760. >
  761. 已審核
  762. </v-chip>
  763. <v-chip
  764. v-else-if="item.is_pass_proposal === 2"
  765. color="error"
  766. text-color="white"
  767. >
  768. 已駁回
  769. </v-chip>
  770. <v-chip v-else> 未審核 </v-chip>
  771. <!-- <small>{{ item.address }}</small> -->
  772. </span>
  773. <small class="mt-2 text-gray">{{ item.address }}</small>
  774. </div>
  775. <input
  776. :disabled="item.is_pass_proposal !== 1"
  777. v-model="assignLocationId"
  778. :true-value="item.location_id"
  779. type="checkbox"
  780. :id="item.location_id"
  781. />
  782. </div>
  783. </li>
  784. </ul>
  785. <!-- <div v-if="schoolSelect.list.length" class="mb-10 school-list">
  786. <v-label class="d-flex align-center w-100 overflow-visible">
  787. <p class="pe-3">已經有據點?</p>
  788. <v-select
  789. v-model="schoolId"
  790. label="請選擇據點"
  791. :items="schoolSelect.list"
  792. item-title="location_name"
  793. item-value="location_id"
  794. hide-details
  795. density="compact"
  796. variant="outlined"
  797. ></v-select>
  798. </v-label>
  799. </div>
  800. <v-row class="justify-space-evenly">
  801. <v-col cols="12" md="5" class="px-0">
  802. <v-label class="d-block">
  803. <p class="d-flex mb-5">
  804. 據點名稱<span class="mark">*</span>
  805. </p>
  806. <v-text-field
  807. v-model="location.location_name"
  808. :rules="[requiredRule]"
  809. title="據點名稱"
  810. placeholder="可以是您的工作室或品牌名稱/教學單位/您的姓名"
  811. density="compact"
  812. variant="outlined"
  813. counter
  814. maxlength="60"
  815. ></v-text-field>
  816. </v-label>
  817. </v-col>
  818. <v-col cols="12" md="5" class="px-0">
  819. <v-label class="d-block">
  820. <p class="d-flex mb-5">
  821. 據點地址<span class="mark">*</span>
  822. </p>
  823. <v-text-field
  824. v-model="location.address"
  825. :rules="[requiredRule]"
  826. title="據點地址"
  827. density="compact"
  828. variant="outlined"
  829. placeholder="需在安全且便於學徒到達之地點開課"
  830. ></v-text-field>
  831. </v-label>
  832. </v-col>
  833. <v-col cols="12" md="5" class="px-0">
  834. <v-label class="d-block">
  835. <p class="d-flex mb-5">
  836. 據點 Email<span class="mark">*</span>
  837. </p>
  838. <v-text-field
  839. v-model="location.email"
  840. :rules="[requiredRule]"
  841. density="compact"
  842. variant="outlined"
  843. placeholder="請填寫 Email"
  844. ></v-text-field>
  845. </v-label>
  846. </v-col>
  847. <v-col cols="12" md="5" class="px-0">
  848. <v-label class="d-block">
  849. <div class="d-flex">
  850. <p class="d-flex mb-5">
  851. 公開電話<span class="mark">*</span>
  852. </p>
  853. <span class="d-block ms-3 hint"
  854. >會顯示於課程介紹中,想了解課程資訊者聯繫用途
  855. </span>
  856. </div>
  857. <v-text-field
  858. v-model="location.phone"
  859. :rules="[requiredRule]"
  860. density="compact"
  861. variant="outlined"
  862. ></v-text-field>
  863. </v-label>
  864. <span class="d-block hint"
  865. >會顯示於課程介紹中,想了解課程資訊者聯繫用途
  866. </span>
  867. </v-col>
  868. <v-col cols="12" md="11">
  869. <v-label class="d-block">
  870. <p class="d-flex mb-5">
  871. 據點簡介<span class="mark">*</span>
  872. </p>
  873. <v-textarea
  874. v-model="location.school_introduction"
  875. :rules="[requiredRule]"
  876. rows="5"
  877. variant="outlined"
  878. ></v-textarea>
  879. </v-label>
  880. </v-col>
  881. </v-row>
  882. <div class="d-flex flex-column justify-end ms-md-16 ps-md-5">
  883. <div
  884. class="map"
  885. id="map"
  886. style="width: 100%; height: 31.25em"
  887. ></div>
  888. <v-row>
  889. <v-col cols="12" md="6">
  890. <v-label class="d-flex align-center">
  891. <p class="pb-5 pe-3">經度</p>
  892. <v-text-field
  893. v-model="location.Lng"
  894. density="compact"
  895. variant="outlined"
  896. disabled
  897. ></v-text-field>
  898. </v-label>
  899. </v-col>
  900. <v-col cols="12" md="6" class="py-0 py-md-3">
  901. <v-label class="d-flex align-center">
  902. <p class="pb-5 pe-3">緯度</p>
  903. <v-text-field
  904. v-model="location.Lat"
  905. density="compact"
  906. variant="outlined"
  907. disabled
  908. ></v-text-field>
  909. </v-label>
  910. </v-col>
  911. </v-row>
  912. </div> -->
  913. </v-form>
  914. </v-card-text>
  915. </v-window-item>
  916. <v-window-item :value="2">
  917. <v-card-text class="resume-content">
  918. <button class="resume-btn">
  919. <p class="d-flex flex-column align-center">
  920. <v-icon icon="mdi-plus" size="x-large"></v-icon>
  921. 新增工藝教育者
  922. </p>
  923. <v-dialog v-model="resumeDialog" activator="parent" width="700">
  924. <v-card class="pa-5">
  925. <v-card-title>工藝教育者履歷</v-card-title>
  926. <v-card-text>
  927. <v-form ref="resumeForm" lazy-validation @submit.prevent>
  928. <v-row>
  929. <v-col cols="12">
  930. <v-label class="d-flex align-center">
  931. <p class="pb-5 pe-3">
  932. 老師姓名<span class="mark">*</span>
  933. </p>
  934. <v-text-field
  935. v-model="resume.teacher_name"
  936. :rules="[requiredRule]"
  937. density="compact"
  938. variant="outlined"
  939. ></v-text-field>
  940. </v-label>
  941. </v-col>
  942. <!-- <v-col cols="6"></v-col> -->
  943. <v-col cols="12">
  944. <v-label class="d-block">
  945. <p class="pb-5 pe-3">
  946. 工作性質<span class="mark">*</span>
  947. </p>
  948. <v-radio-group v-model="resume.work_type" inline>
  949. <v-radio label="全職" value="全職"></v-radio>
  950. <v-radio
  951. label="兼職"
  952. value="兼職"
  953. class="ps-3"
  954. ></v-radio>
  955. </v-radio-group>
  956. </v-label>
  957. </v-col>
  958. <v-col cols="12" class="py-0">
  959. <v-label class="d-block">
  960. <p class="pb-5 pe-3">教學經驗</p>
  961. <v-radio-group v-model="resume.experience" inline>
  962. <v-radio label="0-5 年" value="0-5 年"></v-radio>
  963. <v-radio
  964. label="5-10 年"
  965. value="5-10 年"
  966. class="ps-3"
  967. ></v-radio>
  968. <v-radio
  969. label="10-20 年"
  970. value="10-20 年"
  971. class="ps-3"
  972. ></v-radio>
  973. <v-radio
  974. label="20 年以上"
  975. value="20 年以上"
  976. class="ps-3"
  977. ></v-radio>
  978. </v-radio-group>
  979. </v-label>
  980. </v-col>
  981. <v-col cols="12">
  982. <v-label class="d-block">
  983. <p class="pb-5 pe-3">
  984. 專長工藝技能<span class="mark">*</span>
  985. </p>
  986. <v-text-field
  987. v-model="resume.expertise"
  988. :rules="[requiredRule]"
  989. density="compact"
  990. variant="outlined"
  991. ></v-text-field>
  992. </v-label>
  993. <v-label class="d-block">
  994. <p class="pb-5 pe-3">
  995. 工藝相關證照<span class="mark">*</span>
  996. </p>
  997. <v-text-field
  998. v-model="resume.license"
  999. :rules="[requiredRule]"
  1000. density="compact"
  1001. variant="outlined"
  1002. placeholder="若無工藝相關證照請填「無」"
  1003. ></v-text-field>
  1004. </v-label>
  1005. <v-label class="d-block">
  1006. <p class="d-flex">社群媒體</p>
  1007. <span class="d-block py-3 hint"
  1008. >工藝老師經營之網站或社群媒體,可貼網址</span
  1009. >
  1010. <v-text-field
  1011. v-model="resume.media"
  1012. density="compact"
  1013. variant="outlined"
  1014. ></v-text-field>
  1015. </v-label>
  1016. </v-col>
  1017. <!-- <v-col cols="12">
  1018. <v-label class="d-block">
  1019. <p class="d-flex">講師作品集</p>
  1020. <v-file-input
  1021. multiple
  1022. v-model="portfolioImg"
  1023. ref="portfolioImgRef"
  1024. label="File input"
  1025. variant="outlined"
  1026. placeholder="選擇相片上傳"
  1027. @change="handlePortfolioImg"
  1028. style="display: none"
  1029. ></v-file-input>
  1030. </v-label>
  1031. <v-btn
  1032. @click="fileInputClick('portfolio')"
  1033. color="purple"
  1034. class="my-5"
  1035. >選擇相片上傳</v-btn
  1036. >
  1037. <v-row class="img-list">
  1038. <v-col
  1039. cols="4"
  1040. v-for="(item, index) in 6"
  1041. :key="index"
  1042. >
  1043. <div
  1044. v-if="!portfolioImgList.length"
  1045. class="item"
  1046. ></div>
  1047. <div
  1048. v-else
  1049. class="item"
  1050. :style="{
  1051. backgroundImage: `url('${portfolioImgList[index]}')`,
  1052. }"
  1053. ></div>
  1054. </v-col>
  1055. </v-row>
  1056. </v-col> -->
  1057. <v-col cols="12">
  1058. <v-label class="d-block">
  1059. <p class="d-flex mb-5">
  1060. 工藝教育者介紹<span class="mark">*</span>
  1061. </p>
  1062. <v-textarea
  1063. v-model="resume.introduction"
  1064. :rules="[
  1065. (value) =>
  1066. (value && value.length >= 50) ||
  1067. '請至少輸入 50 個字元,以確保內容完整性',
  1068. ]"
  1069. rows="5"
  1070. variant="outlined"
  1071. hint="文字長度不得低於 50 個字元,最多 150 個字元"
  1072. maxlength="150"
  1073. counter
  1074. ></v-textarea>
  1075. </v-label>
  1076. </v-col>
  1077. </v-row>
  1078. </v-form>
  1079. </v-card-text>
  1080. <v-card-actions class="justify-center">
  1081. <v-spacer></v-spacer>
  1082. <v-btn text="取消" @click="resumeDialog = false"></v-btn>
  1083. <v-btn
  1084. @click="insertResume()"
  1085. color="purple"
  1086. variant="flat"
  1087. >
  1088. 新增
  1089. </v-btn>
  1090. </v-card-actions>
  1091. </v-card>
  1092. </v-dialog>
  1093. </button>
  1094. <h6 data-v-bbf03f1f="" class="mt-10 table-title">工藝教育者履歷</h6>
  1095. <div class="main-table">
  1096. <!-- <h6 data-v-bbf03f1f="" class="table-title">工藝教育者履歷</h6> -->
  1097. <table>
  1098. <thead>
  1099. <tr>
  1100. <th>姓名</th>
  1101. <th>工作性質</th>
  1102. <th>教學經驗</th>
  1103. <th>專長工藝技能</th>
  1104. <th>工藝相關證照</th>
  1105. <th>社群媒體</th>
  1106. <th style="width: 30%">老師介紹</th>
  1107. <th></th>
  1108. <!-- <th width="15%">繳款資訊</th> -->
  1109. </tr>
  1110. </thead>
  1111. <tbody v-if="resumeList.length">
  1112. <!-- <tr v-if="!register.list.length">
  1113. <p class="hint-item">
  1114. 目前沒有報名紀錄喔!點擊開始探索您的專屬課程!
  1115. </p>
  1116. </tr> -->
  1117. <tr
  1118. v-for="(item, index) in resumeList"
  1119. :key="index"
  1120. @click="assignTeacher(item, index)"
  1121. >
  1122. <td>{{ item.teacher_name }}</td>
  1123. <td>{{ item.work_type }}</td>
  1124. <td>{{ item.experience }}</td>
  1125. <td>{{ item.expertise }}</td>
  1126. <td>{{ item.license }}</td>
  1127. <td>{{ item.media }}</td>
  1128. <td>
  1129. <v-dialog width="600">
  1130. <template v-slot:activator="{ props }">
  1131. <v-btn
  1132. v-bind="props"
  1133. text="查看"
  1134. variant="tonal"
  1135. color="purple"
  1136. rounded="xl"
  1137. >
  1138. </v-btn>
  1139. </template>
  1140. <template v-slot:default="{ isActive }">
  1141. <v-card title="老師介紹" class="pa-3">
  1142. <v-card-text
  1143. style="letter-spacing: 1px; line-height: 1.5"
  1144. >
  1145. {{ item.introduction }}
  1146. </v-card-text>
  1147. <v-card-actions class="justify-end">
  1148. <v-spacer></v-spacer>
  1149. <v-btn
  1150. text="關閉"
  1151. @click="isActive.value = false"
  1152. ></v-btn>
  1153. </v-card-actions>
  1154. </v-card>
  1155. </template>
  1156. </v-dialog>
  1157. </td>
  1158. <td>
  1159. <input
  1160. v-model="teacherCheckbox[index]"
  1161. type="checkbox"
  1162. class="me-5"
  1163. />
  1164. </td>
  1165. </tr>
  1166. </tbody>
  1167. </table>
  1168. </div>
  1169. </v-card-text>
  1170. <!-- <v-row align="center" justify="center" class="mt-10 mb-16">
  1171. <v-col
  1172. v-for="(item, index) in teachers.list"
  1173. :key="item"
  1174. cols="auto"
  1175. >
  1176. <v-card
  1177. @click="assignTeacher(item, index)"
  1178. class="mx-auto pa-3 teachers-card"
  1179. width="330"
  1180. >
  1181. <v-card-item>
  1182. <div>
  1183. <div
  1184. class="d-flex justify-space-between align-center mb-4 pe-1"
  1185. >
  1186. <h4>{{ item.teacher_name }}</h4>
  1187. <input
  1188. v-model="teacherCheckbox[index]"
  1189. type="checkbox"
  1190. name=""
  1191. id=""
  1192. />
  1193. </div>
  1194. <ul class="pt-4">
  1195. <li>工作性質: {{ item.work_type }}</li>
  1196. <li>工藝技能:{{ item.expertise }}</li>
  1197. <li>工藝證照:{{ item.license }}</li>
  1198. <li>教學經驗:{{ item.experience }}</li>
  1199. </ul>
  1200. </div>
  1201. </v-card-item>
  1202. </v-card>
  1203. </v-col>
  1204. </v-row> -->
  1205. <!-- <v-card-text>
  1206. <v-form ref="resumeForm" lazy-validation @submit.prevent>
  1207. <v-row>
  1208. <v-col cols="12" sm="6">
  1209. <v-label class="d-flex align-center pb-3">
  1210. <p class="pb-5 pe-3">老師姓名<span class="mark">*</span></p>
  1211. <v-text-field
  1212. v-model="resume.teacher_name"
  1213. :rules="[requiredRule]"
  1214. density="compact"
  1215. variant="outlined"
  1216. ></v-text-field>
  1217. </v-label>
  1218. </v-col>
  1219. <v-col cols="6"></v-col>
  1220. <v-col cols="12" lg="3">
  1221. <v-label class="d-block">
  1222. <p class="pb-5 pe-3">工作性質<span class="mark">*</span></p>
  1223. <v-radio-group v-model="resume.work_type" inline>
  1224. <v-radio label="全職" value="全職"></v-radio>
  1225. <v-radio label="兼職" value="兼職" class="ps-3"></v-radio>
  1226. </v-radio-group>
  1227. </v-label>
  1228. </v-col>
  1229. <v-col cols="12" lg="9">
  1230. <v-label class="d-block">
  1231. <p class="pb-5 pe-3">教學經驗</p>
  1232. <v-radio-group v-model="resume.experience" inline>
  1233. <v-radio label="0-5 年" value="0-5 年"></v-radio>
  1234. <v-radio
  1235. label="5-10 年"
  1236. value="5-10 年"
  1237. class="ps-3"
  1238. ></v-radio>
  1239. <v-radio
  1240. label="10-20 年"
  1241. value="10-20 年"
  1242. class="ps-3"
  1243. ></v-radio>
  1244. <v-radio
  1245. label="20 年以上"
  1246. value="20 年以上"
  1247. class="ps-3"
  1248. ></v-radio>
  1249. </v-radio-group>
  1250. </v-label>
  1251. </v-col>
  1252. <v-col cols="12" md="6">
  1253. <v-label class="d-block">
  1254. <p class="pb-5 pe-3">
  1255. 專長工藝技能<span class="mark">*</span>
  1256. </p>
  1257. <v-text-field
  1258. v-model="resume.expertise"
  1259. :rules="[requiredRule]"
  1260. density="compact"
  1261. variant="outlined"
  1262. ></v-text-field>
  1263. </v-label>
  1264. <v-label class="d-block">
  1265. <p class="pb-5 pe-3">工藝相關證照</p>
  1266. <v-text-field
  1267. v-model="resume.license"
  1268. density="compact"
  1269. variant="outlined"
  1270. ></v-text-field>
  1271. </v-label>
  1272. <v-label class="d-block">
  1273. <p class="d-flex">社群媒體</p>
  1274. <span class="d-block py-3 hint"
  1275. >工藝老師經營之網站或社群媒體,可貼網址</span
  1276. >
  1277. <v-text-field
  1278. v-model="resume.media"
  1279. density="compact"
  1280. variant="outlined"
  1281. ></v-text-field>
  1282. </v-label>
  1283. </v-col>
  1284. <v-col cols="12" md="6">
  1285. <v-label class="d-block">
  1286. <p class="d-flex">講師作品集</p>
  1287. <v-file-input
  1288. multiple
  1289. v-model="portfolioImg"
  1290. ref="portfolioImgRef"
  1291. label="File input"
  1292. variant="outlined"
  1293. placeholder="選擇相片上傳"
  1294. @change="handlePortfolioImg"
  1295. style="display: none"
  1296. ></v-file-input>
  1297. </v-label>
  1298. <v-btn
  1299. @click="fileInputClick('portfolio')"
  1300. color="purple"
  1301. class="my-5"
  1302. >選擇相片上傳</v-btn
  1303. >
  1304. <v-row class="img-list">
  1305. <v-col cols="4" v-for="(item, index) in 6" :key="index">
  1306. <div v-if="!portfolioImgList.length" class="item"></div>
  1307. <div
  1308. v-else
  1309. class="item"
  1310. :style="{
  1311. backgroundImage: `url('${portfolioImgList[index]}')`,
  1312. }"
  1313. ></div>
  1314. </v-col>
  1315. </v-row>
  1316. </v-col>
  1317. <v-col cols="12">
  1318. <v-label class="d-block">
  1319. <p class="d-flex mb-5">老師自我介紹</p>
  1320. <v-textarea
  1321. v-model="resume.introduction"
  1322. rows="5"
  1323. variant="outlined"
  1324. ></v-textarea>
  1325. </v-label>
  1326. </v-col>
  1327. </v-row>
  1328. </v-form>
  1329. </v-card-text> -->
  1330. </v-window-item>
  1331. <v-window-item :value="3">
  1332. <v-card-text class="mb-7">
  1333. <v-form ref="courseForm" lazy-validation @submit.prevent>
  1334. <v-row class="justify-space-evenly">
  1335. <v-col cols="12" md="6">
  1336. <v-row>
  1337. <v-col cols="12">
  1338. <v-label class="d-block">
  1339. <p class="pb-5 pe-3">
  1340. 課程名稱<span class="mark">*</span>
  1341. </p>
  1342. <v-text-field
  1343. v-model="course.name"
  1344. :rules="[requiredRule]"
  1345. density="compact"
  1346. variant="outlined"
  1347. ></v-text-field>
  1348. </v-label>
  1349. </v-col>
  1350. <v-col cols="12">
  1351. <v-label class="d-block">
  1352. <p class="pb-5 pe-3">
  1353. 工藝類別<span class="mark">*</span>
  1354. </p>
  1355. <v-select
  1356. v-model="course.category"
  1357. placeholder="請選擇類別"
  1358. :items="[
  1359. '樹藝',
  1360. '漆藝',
  1361. '藍染',
  1362. '蠟雕',
  1363. '竹工藝籃',
  1364. '金工/飾品',
  1365. '蠟燭/香氛/調香',
  1366. '植栽/花藝',
  1367. '插畫/繪畫/寫字',
  1368. '皮件/皮革',
  1369. '木工/竹藝',
  1370. '陶藝/玻璃',
  1371. '編織/羊毛氈/縫紉',
  1372. '其他',
  1373. ]"
  1374. :rules="[requiredRule]"
  1375. density="compact"
  1376. variant="outlined"
  1377. ></v-select>
  1378. </v-label>
  1379. </v-col>
  1380. <v-col cols="12">
  1381. <v-label class="d-block">
  1382. <p class="pb-5 pe-3">上課地點</p>
  1383. <v-text-field
  1384. v-model="locationData.address"
  1385. label="自動帶入據點地址"
  1386. density="compact"
  1387. variant="outlined"
  1388. disabled
  1389. ></v-text-field>
  1390. </v-label>
  1391. </v-col>
  1392. <v-col cols="12">
  1393. <v-label class="d-block">
  1394. <p class="pb-5 pe-3">主辦單位</p>
  1395. <v-text-field
  1396. v-model="locationData.location_name"
  1397. label="自動帶入據點名稱"
  1398. density="compact"
  1399. variant="outlined"
  1400. disabled
  1401. ></v-text-field>
  1402. </v-label>
  1403. </v-col>
  1404. <v-col cols="12">
  1405. <v-label class="d-block">
  1406. <p class="d-flex">
  1407. 課程時間<span class="mark">*</span>
  1408. </p>
  1409. <v-dialog
  1410. v-model="sessionsDialog"
  1411. persistent
  1412. width="800"
  1413. >
  1414. <template v-slot:activator="{ props }">
  1415. <v-btn class="my-5" color="purple" v-bind="props">
  1416. <v-icon icon="mdi-plus" class="me-1"></v-icon>
  1417. 建立新場次
  1418. </v-btn>
  1419. </template>
  1420. <v-card class="sessions-card pb-3">
  1421. <!-- <v-card-title>
  1422. <span class="d-block ps-10 py-5">建立新場次</span>
  1423. </v-card-title> -->
  1424. <v-card-text class="pa-8">
  1425. <v-form
  1426. ref="eventForm"
  1427. lazy-validation
  1428. @submit.prevent
  1429. >
  1430. <v-container class="py-0">
  1431. <v-row>
  1432. <v-col cols="12">
  1433. <p class="text-h6 font-weight-bold my-3">
  1434. 建立新場次
  1435. </p>
  1436. </v-col>
  1437. <!-- <v-col cols="12" sm="6" class="date-item">
  1438. <p class="mb-0 pe-3">
  1439. 起始日期<span class="mark">*</span>
  1440. </p>
  1441. <VueDatePicker
  1442. v-model="date.start_date"
  1443. :min-date="new Date()"
  1444. :enable-time-picker="false"
  1445. :format="store.datePickerFormat"
  1446. locale="cn"
  1447. ></VueDatePicker>
  1448. </v-col>
  1449. <v-col cols="12" sm="6" class="date-item">
  1450. <p class="mb-0 pe-3">
  1451. 起始時間<span class="mark">*</span>
  1452. </p>
  1453. <VueDatePicker
  1454. v-model="date.start_time"
  1455. time-picker
  1456. />
  1457. </v-col>
  1458. <v-col cols="12" sm="6" class="date-item">
  1459. <p class="mb-0 pe-3">
  1460. 結束日期<span class="mark">*</span>
  1461. </p>
  1462. <VueDatePicker
  1463. v-model="date.end_date"
  1464. :min-date="new Date()"
  1465. :enable-time-picker="false"
  1466. :format="store.datePickerFormat"
  1467. locale="cn"
  1468. ></VueDatePicker>
  1469. </v-col>
  1470. <v-col cols="12" sm="6" class="date-item">
  1471. <p class="mb-0 pe-3">
  1472. 結束時間<span class="mark">*</span>
  1473. </p>
  1474. <VueDatePicker
  1475. v-model="date.end_time"
  1476. time-picker
  1477. />
  1478. </v-col> -->
  1479. <v-col cols="12" sm="6" class="py-0">
  1480. <v-label class="d-flex align-center py-2">
  1481. <p class="pb-5 pe-3">
  1482. 場次名稱<span class="mark">*</span>
  1483. </p>
  1484. <v-text-field
  1485. v-model="event.event"
  1486. :rules="[requiredRule]"
  1487. density="compact"
  1488. variant="outlined"
  1489. ></v-text-field>
  1490. </v-label>
  1491. </v-col>
  1492. <v-col cols="12" sm="6" class="py-0">
  1493. <v-label class="d-flex align-center py-2">
  1494. <p class="pb-5 pe-3">
  1495. 課程講師<span class="mark">*</span>
  1496. </p>
  1497. <v-text-field
  1498. v-model="event.lecturer"
  1499. :rules="[requiredRule]"
  1500. density="compact"
  1501. variant="outlined"
  1502. ></v-text-field>
  1503. </v-label>
  1504. </v-col>
  1505. <v-col cols="12" sm="6" class="py-0">
  1506. <v-label class="d-flex align-center py-2">
  1507. <p class="pb-5 pe-3">
  1508. 聯絡資訊<span class="mark">*</span>
  1509. </p>
  1510. <v-text-field
  1511. v-model="event.contact"
  1512. :rules="[requiredRule]"
  1513. density="compact"
  1514. variant="outlined"
  1515. ></v-text-field>
  1516. </v-label>
  1517. </v-col>
  1518. <v-col cols="12" sm="6" class="py-0">
  1519. <v-label class="d-flex align-center py-2">
  1520. <p class="pb-5 pe-3">課程教室</p>
  1521. <v-text-field
  1522. v-model="event.location"
  1523. density="compact"
  1524. variant="outlined"
  1525. ></v-text-field>
  1526. </v-label>
  1527. </v-col>
  1528. <v-col cols="12" sm="6" class="py-0">
  1529. <v-label class="d-flex align-center py-2">
  1530. <p class="pb-5 pe-3">
  1531. 課程名額<span class="mark">*</span>
  1532. </p>
  1533. <v-text-field
  1534. v-model="event.number_limit"
  1535. :rules="[requiredRule]"
  1536. density="compact"
  1537. variant="outlined"
  1538. type="number"
  1539. placeholder="請設定報名人數上限"
  1540. ></v-text-field>
  1541. </v-label>
  1542. </v-col>
  1543. <v-col cols="12" sm="6" class="py-0">
  1544. <v-label class="d-flex align-center py-2">
  1545. <p class="pb-5 pe-3">
  1546. 最低開課人數<span class="mark"
  1547. >*</span
  1548. >
  1549. </p>
  1550. <v-text-field
  1551. density="compact"
  1552. variant="outlined"
  1553. type="number"
  1554. placeholder="請輸入開課門檻人數"
  1555. ></v-text-field>
  1556. </v-label>
  1557. </v-col>
  1558. <v-col cols="12" class="py-0">
  1559. <v-label class="d-flex align-center py-2">
  1560. <p class="pb-5 pe-3">
  1561. 收費方式<span class="mark">*</span>
  1562. </p>
  1563. <v-select
  1564. v-model="event.fee_payment"
  1565. :items="['現場收費', '匯款']"
  1566. :rules="[requiredRule]"
  1567. density="compact"
  1568. variant="outlined"
  1569. outlined
  1570. ></v-select>
  1571. </v-label>
  1572. </v-col>
  1573. <v-col cols="12" class="py-0">
  1574. <v-label
  1575. v-if="isRemit"
  1576. class="d-flex align-center py-2"
  1577. >
  1578. <p class="pb-5 pe-3">
  1579. 匯款資訊<span class="mark">*</span>
  1580. </p>
  1581. <div class="d-flex w-100">
  1582. <v-text-field
  1583. v-model="bankCode"
  1584. label="銀行代碼"
  1585. :rules="[requiredRule]"
  1586. density="compact"
  1587. variant="outlined"
  1588. style="width: 30%"
  1589. class="me-3"
  1590. ></v-text-field>
  1591. <v-text-field
  1592. v-model="bankAccount"
  1593. label="匯款帳號 (10-16 碼)"
  1594. :rules="[requiredRule]"
  1595. density="compact"
  1596. variant="outlined"
  1597. style="width: 70%"
  1598. ></v-text-field>
  1599. </div>
  1600. </v-label>
  1601. </v-col>
  1602. <v-col cols="12" class="py-0">
  1603. <v-label class="d-flex align-center py-2">
  1604. <p class="pb-5 pe-3">
  1605. 課程價格<span class="mark">*</span>
  1606. </p>
  1607. <v-text-field
  1608. v-model="event.fee_method"
  1609. :rules="[requiredRule]"
  1610. density="compact"
  1611. variant="outlined"
  1612. type="number"
  1613. ></v-text-field>
  1614. </v-label>
  1615. </v-col>
  1616. <v-col cols="12" class="py-0">
  1617. <v-label class="d-flex align-center py-2">
  1618. <p class="pb-3 pe-3">
  1619. 適合對象<span class="mark pb-4"
  1620. >*</span
  1621. >
  1622. </p>
  1623. <v-textarea
  1624. rows="2"
  1625. variant="outlined"
  1626. placeholder="ex. 不拘、18-65 歲、大專院校工藝、美術、設計科系學生、從事木作相關之業者或個人工作室"
  1627. :rules="[requiredRule]"
  1628. ></v-textarea>
  1629. <!-- <v-select
  1630. v-model="event.people"
  1631. :items="[
  1632. '不拘',
  1633. '7 歲以下',
  1634. '7-18 歲',
  1635. '18-65 歲',
  1636. '65 歲以上',
  1637. ]"
  1638. :rules="[requiredRule]"
  1639. density="compact"
  1640. variant="outlined"
  1641. outlined
  1642. ></v-select> -->
  1643. </v-label>
  1644. </v-col>
  1645. <v-col cols="12" class="py-0">
  1646. <v-label class="d-flex align-center py-2">
  1647. <p class="ps-8 pe-5">備註</p>
  1648. <v-text-field
  1649. v-model="event.remark"
  1650. density="compact"
  1651. variant="outlined"
  1652. hide-details
  1653. ></v-text-field>
  1654. </v-label>
  1655. </v-col>
  1656. <v-divider class="my-10"></v-divider>
  1657. <v-col cols="12" class="pt-0">
  1658. <p class="text-h6 font-weight-bold mb-3">
  1659. 課程時間
  1660. </p>
  1661. </v-col>
  1662. <v-col
  1663. cols="12"
  1664. class="py-0 position-relative"
  1665. >
  1666. <v-label class="d-flex align-center pt-2">
  1667. <p class="pb-3 pe-3">
  1668. 課程類型<span class="mark pb-4"
  1669. >*</span
  1670. >
  1671. </p>
  1672. <v-select
  1673. v-model="eventType"
  1674. :items="[
  1675. '一日課程(例:2023/10/1 週一上課)',
  1676. '週期課程(例:2023/10/1~2023/10/30 每週一三上課)',
  1677. ]"
  1678. :rules="[requiredRule]"
  1679. density="compact"
  1680. variant="outlined"
  1681. outlined
  1682. ></v-select>
  1683. </v-label>
  1684. <!-- <small
  1685. v-show="eventType !== ''"
  1686. class="type-hint"
  1687. >{{
  1688. isOneDay
  1689. ? "例:2023/10/2 週一上課"
  1690. : "例:2023/10/1~2023/10/30 每週一三上課"
  1691. }}</small
  1692. > -->
  1693. </v-col>
  1694. <v-col
  1695. v-if="!isOneDay"
  1696. cols="12"
  1697. class="d-flex py-0"
  1698. >
  1699. <p class="pt-1 pe-3">
  1700. 重複週期<span class="mark pb-4">*</span>
  1701. </p>
  1702. <ul class="week-list">
  1703. <li
  1704. v-for="(item, index) in weekList"
  1705. :key="index"
  1706. @click="toggleSelected(index)"
  1707. :class="{
  1708. active: selectedWeek[index],
  1709. }"
  1710. class="d-flex"
  1711. >
  1712. <button class="item">
  1713. {{ item }}
  1714. </button>
  1715. <div
  1716. v-if="selectedWeek[index]"
  1717. class="d-flex w-100 ms-5"
  1718. >
  1719. <VueDatePicker
  1720. v-model="
  1721. sessionTime.start_week_time[
  1722. index
  1723. ]
  1724. "
  1725. time-picker
  1726. />
  1727. <span
  1728. class="d-flex align-center mx-2"
  1729. >~</span
  1730. >
  1731. <VueDatePicker
  1732. v-model="
  1733. sessionTime.end_week_time[index]
  1734. "
  1735. time-picker
  1736. />
  1737. </div>
  1738. </li>
  1739. </ul>
  1740. </v-col>
  1741. <v-col cols="12" sm="6" class="date-item">
  1742. <p class="mb-0 pe-3">
  1743. 起始日期<span class="mark">*</span>
  1744. </p>
  1745. <VueDatePicker
  1746. v-model="date.start_date"
  1747. :min-date="new Date()"
  1748. :enable-time-picker="false"
  1749. :format="store.datePickerFormat"
  1750. locale="cn"
  1751. ></VueDatePicker>
  1752. </v-col>
  1753. <v-col cols="12" sm="6" class="date-item">
  1754. <p class="mb-0 pe-3">
  1755. 起始時間<span class="mark">*</span>
  1756. </p>
  1757. <VueDatePicker
  1758. v-model="date.start_time"
  1759. time-picker
  1760. />
  1761. </v-col>
  1762. <v-col
  1763. cols="12"
  1764. sm="6"
  1765. class="date-item position-relative"
  1766. >
  1767. <p class="mb-0 pe-3">
  1768. 結束日期<span class="mark">*</span>
  1769. </p>
  1770. <VueDatePicker
  1771. v-model="date.end_date"
  1772. :min-date="new Date()"
  1773. :enable-time-picker="false"
  1774. :format="store.datePickerFormat"
  1775. locale="cn"
  1776. :disabled="isOneDay"
  1777. ></VueDatePicker>
  1778. <span
  1779. v-if="dateReminder"
  1780. class="date-reminder text-error"
  1781. >選擇週期課程時,起始日期與結束日期不可為同一天</span
  1782. >
  1783. </v-col>
  1784. <v-col cols="12" sm="6" class="date-item">
  1785. <p class="mb-0 pe-3">
  1786. 結束時間<span class="mark">*</span>
  1787. </p>
  1788. <VueDatePicker
  1789. v-model="date.end_time"
  1790. time-picker
  1791. />
  1792. </v-col>
  1793. <!-- <v-col
  1794. v-if="!isOneDay"
  1795. cols="12"
  1796. class="time-item pt-5 pb-7"
  1797. >
  1798. <p class="mb-0">
  1799. 課程時間<span class="mark">*</span>
  1800. </p>
  1801. <div class="d-flex w-100">
  1802. <VueDatePicker
  1803. v-model="sessionTime.start_week_time"
  1804. time-picker
  1805. />
  1806. <span class="d-flex align-center mx-2"
  1807. >~</span
  1808. >
  1809. <VueDatePicker
  1810. v-model="sessionTime.end_week_time"
  1811. time-picker
  1812. />
  1813. </div>
  1814. </v-col> -->
  1815. <v-divider class="my-10"></v-divider>
  1816. <v-col cols="12" class="pt-0">
  1817. <p class="text-h6 font-weight-bold mb-3">
  1818. 報名時間
  1819. </p>
  1820. </v-col>
  1821. <v-col cols="12" sm="6" class="date-item">
  1822. <p class="mb-0 pe-3">
  1823. 報名日期<span class="mark pb-4">*</span>
  1824. </p>
  1825. <VueDatePicker
  1826. v-model="date.registration_start_date"
  1827. :enable-time-picker="false"
  1828. :format="store.datePickerFormat"
  1829. locale="cn"
  1830. ></VueDatePicker>
  1831. </v-col>
  1832. <v-col cols="12" sm="6" class="date-item">
  1833. <p class="mb-0 pe-3">
  1834. 報名時間<span class="mark pb-4">*</span>
  1835. </p>
  1836. <VueDatePicker
  1837. v-model="date.registration_start_time"
  1838. time-picker
  1839. />
  1840. </v-col>
  1841. <v-col cols="12" sm="6" class="date-item">
  1842. <p class="mb-0 pe-3">
  1843. 報名截止<span class="mark pb-4">*</span>
  1844. </p>
  1845. <VueDatePicker
  1846. v-model="date.registration_end_date"
  1847. :enable-time-picker="false"
  1848. :format="store.datePickerFormat"
  1849. locale="cn"
  1850. ></VueDatePicker>
  1851. </v-col>
  1852. <v-col cols="12" sm="6" class="date-item">
  1853. <p class="mb-0 pe-3">
  1854. 截止時間<span class="mark pb-4">*</span>
  1855. </p>
  1856. <VueDatePicker
  1857. v-model="date.registration_end_time"
  1858. time-picker
  1859. />
  1860. </v-col>
  1861. </v-row>
  1862. </v-container>
  1863. </v-form>
  1864. </v-card-text>
  1865. <v-card-actions class="justify-center pt-10 pb-5">
  1866. <v-btn
  1867. color="gray"
  1868. variant="tonal"
  1869. class="me-3 px-10"
  1870. @click="sessionsDialog = false"
  1871. >
  1872. 取消
  1873. </v-btn>
  1874. <v-btn
  1875. color="purple"
  1876. variant="flat"
  1877. @click="addEventData()"
  1878. class="px-10"
  1879. >
  1880. 新增
  1881. </v-btn>
  1882. </v-card-actions>
  1883. </v-card>
  1884. </v-dialog>
  1885. <div v-if="eventData.list.length" class="main-table">
  1886. <h6 class="table-title">場次資訊</h6>
  1887. <table>
  1888. <thead>
  1889. <tr>
  1890. <th>名稱</th>
  1891. <th>日期</th>
  1892. <th width="20%">課程講師</th>
  1893. <th width="20%">收費方式</th>
  1894. </tr>
  1895. </thead>
  1896. <tbody>
  1897. <tr
  1898. v-for="(item, index) in eventData.list"
  1899. :key="index"
  1900. >
  1901. <td>{{ item.event }}</td>
  1902. <td>
  1903. {{
  1904. moment(`${item.start_time}`).format(
  1905. "YYYY/MM/DD"
  1906. )
  1907. }}
  1908. <br />
  1909. <br />
  1910. {{
  1911. moment(`${item.end_time}`).format(
  1912. "YYYY/MM/DD"
  1913. )
  1914. }}
  1915. </td>
  1916. <td>{{ item.lecturer }}</td>
  1917. <td>{{ item.fee_method }}</td>
  1918. </tr>
  1919. </tbody>
  1920. </table>
  1921. </div>
  1922. </v-label>
  1923. </v-col>
  1924. <v-col cols="12">
  1925. <v-label class="d-block">
  1926. <p class="d-flex">
  1927. 課程簡介<span class="mark">*</span>
  1928. </p>
  1929. <small class="d-block text-gray my-2"
  1930. >內容不得少於 100
  1931. 個字元,且不得含有工藝無關之資訊</small
  1932. >
  1933. <v-textarea
  1934. v-model="course.introduction"
  1935. rows="5"
  1936. variant="outlined"
  1937. :rules="[
  1938. (value) =>
  1939. (value && value.length >= 100) ||
  1940. '請至少輸入 100 個字元,以確保內容完整性',
  1941. ]"
  1942. counter
  1943. ></v-textarea>
  1944. </v-label>
  1945. </v-col>
  1946. <v-col cols="12">
  1947. <v-label class="d-block">
  1948. <div class="d-flex align-center">
  1949. <p class="d-flex me-3">
  1950. 課程內容 (課綱) <span class="mark">*</span>
  1951. </p>
  1952. <v-dialog width="500">
  1953. <template v-slot:activator="{ props }">
  1954. <v-btn
  1955. v-bind="props"
  1956. variant="outlined"
  1957. color="purple"
  1958. >查看範例
  1959. </v-btn>
  1960. </template>
  1961. <template v-slot:default="{ isActive }">
  1962. <v-card>
  1963. <v-card-text>
  1964. <div class="d-flex justify-end">
  1965. <v-btn
  1966. @click="isActive.value = false"
  1967. icon="mdi-close"
  1968. variant="text"
  1969. ></v-btn>
  1970. </div>
  1971. <ul class="section-list pa-3">
  1972. <li>章節 1 — 技法示範與研磨實作,上漆</li>
  1973. <li>章節 2 — 研磨實作</li>
  1974. <li>章節 3 — 推光、擦漆</li>
  1975. <li>章節 4 — 作品總整理</li>
  1976. </ul>
  1977. </v-card-text>
  1978. </v-card>
  1979. </template>
  1980. </v-dialog>
  1981. </div>
  1982. <!-- <v-row class="my-5">
  1983. <v-col cols="6">
  1984. <v-text-field
  1985. v-model="syllabusData.title"
  1986. density="compact"
  1987. variant="outlined"
  1988. hide-details
  1989. placeholder="章節標題"
  1990. ></v-text-field>
  1991. </v-col>
  1992. <v-col cols="6">
  1993. <v-text-field
  1994. v-model="syllabusData.content"
  1995. density="compact"
  1996. variant="outlined"
  1997. hide-details
  1998. placeholder="備註內容(非必填)"
  1999. ></v-text-field>
  2000. </v-col>
  2001. </v-row> -->
  2002. <div class="d-flex my-5">
  2003. <div class="w-100">
  2004. <v-text-field
  2005. v-model="section"
  2006. density="compact"
  2007. variant="outlined"
  2008. hide-details
  2009. placeholder="章節標題"
  2010. ></v-text-field>
  2011. <!-- <v-textarea
  2012. v-model="syllabusData.content"
  2013. density="compact"
  2014. variant="outlined"
  2015. hide-details
  2016. placeholder="章節內容"
  2017. rows="3"
  2018. counter
  2019. ></v-textarea> -->
  2020. </div>
  2021. <v-btn
  2022. color="purple"
  2023. icon="mdi-plus"
  2024. size="small"
  2025. class="ms-5"
  2026. @click="addSection()"
  2027. ></v-btn>
  2028. </div>
  2029. <ul class="section-list">
  2030. <li
  2031. v-for="(item, index) in sectionList"
  2032. :key="index"
  2033. class="py-2"
  2034. >
  2035. <div
  2036. class="d-flex align-center justify-space-between"
  2037. >
  2038. <p>章節 {{ index + 1 }} — {{ item }}</p>
  2039. <v-btn
  2040. color="error"
  2041. icon="mdi-close"
  2042. variant="text"
  2043. size="small"
  2044. @click="addSection()"
  2045. ></v-btn>
  2046. </div>
  2047. </li>
  2048. </ul>
  2049. <!-- <ul>
  2050. <li v-for="(item, index) in sectionList" :key="index">
  2051. 章節 {{ index + 1 }} — {{ item }}
  2052. </li>
  2053. </ul> -->
  2054. <!-- <v-textarea
  2055. v-model="syllabusData.content"
  2056. density="compact"
  2057. variant="outlined"
  2058. hide-details
  2059. placeholder="備註內容(非必填)"
  2060. rows="2"
  2061. counter
  2062. ></v-textarea> -->
  2063. </v-label>
  2064. </v-col>
  2065. <!-- <v-col cols="12">
  2066. <v-label class="d-block">
  2067. <p class="d-flex">章節預覽:</p>
  2068. </v-label>
  2069. </v-col> -->
  2070. <v-col cols="12" class="me-auto">
  2071. <v-label class="d-block">
  2072. <p class="d-flex">
  2073. 上傳課程封面<span class="mark">*</span>
  2074. </p>
  2075. <v-file-input
  2076. multiple
  2077. v-model="coverImg"
  2078. ref="coverImgRef"
  2079. label="File input"
  2080. variant="outlined"
  2081. placeholder="選擇相片上傳"
  2082. @change="handleCoverImg"
  2083. style="display: none"
  2084. ></v-file-input>
  2085. </v-label>
  2086. <v-btn
  2087. @click="fileInputClick('cover')"
  2088. color="purple"
  2089. class="my-5"
  2090. >選擇相片上傳</v-btn
  2091. >
  2092. <div class="step-01 image-preview">
  2093. <img
  2094. v-if="coverImg"
  2095. :src="coverImgUrl"
  2096. alt="上傳圖片預覽"
  2097. />
  2098. </div>
  2099. <!-- <v-row class="img-list">
  2100. <v-col cols="4" v-for="(item, index) in 6" :key="index">
  2101. <div
  2102. v-if="!portfolioImgList.length"
  2103. class="item"
  2104. ></div>
  2105. <div
  2106. v-else
  2107. class="item"
  2108. :style="{
  2109. backgroundImage: `url('${portfolioImgList[index]}')`,
  2110. }"
  2111. ></div>
  2112. </v-col>
  2113. </v-row> -->
  2114. </v-col>
  2115. </v-row>
  2116. </v-col>
  2117. <v-col cols="12" md="6">
  2118. <div class="notes-block">
  2119. <p class="mb-5">課程審核標準:</p>
  2120. <ul>
  2121. <li>
  2122. 課程內容正確合宜,無錯誤與誇大不實、不含與課程無關的推銷廣告、內容無抄襲侵權、無涉及任何色情、血腥暴力、人身攻擊、歧視(包含性別、種族、性傾向等)。
  2123. </li>
  2124. <li>◆ 提供圖片需與課程內容相符。</li>
  2125. <li>◆ 教學內容符合邏輯。</li>
  2126. </ul>
  2127. </div>
  2128. </v-col>
  2129. </v-row>
  2130. </v-form>
  2131. </v-card-text>
  2132. </v-window-item>
  2133. <v-window-item :value="4">
  2134. <v-card-text class="mb-7 finish-step">
  2135. <p>
  2136. 請等待後台人員確認您的課程資訊 <br />
  2137. 待您收到開課完成的 Email <br />
  2138. 就可以前往
  2139. <router-link to="/user/courses">【我的開課】</router-link>
  2140. 觀看及修改您的
  2141. </p>
  2142. <ul>
  2143. <li>(1) 據點資訊</li>
  2144. <li>(2) 工藝教育者履歷</li>
  2145. <li>(3) 課程清單</li>
  2146. </ul>
  2147. </v-card-text>
  2148. </v-window-item>
  2149. </v-window>
  2150. <v-divider></v-divider>
  2151. <v-card-actions
  2152. class="justify-center flex-column flex-sm-row mt-5 mt-sm-7"
  2153. >
  2154. <v-btn
  2155. v-if="step > 1 && step !== 4"
  2156. color="gray"
  2157. variant="outlined"
  2158. size="large"
  2159. @click="step--"
  2160. class="px-7 me-7"
  2161. >
  2162. 上一步
  2163. </v-btn>
  2164. <v-spacer></v-spacer>
  2165. <v-btn
  2166. v-if="step < 3"
  2167. color="purple"
  2168. variant="flat"
  2169. size="large"
  2170. @click="checkField(step)"
  2171. class="px-7"
  2172. >
  2173. 下一步
  2174. </v-btn>
  2175. <!-- <div
  2176. v-if="errorField"
  2177. class="d-flex align-center text-error mt-3 position-absolute"
  2178. style="bottom: 15px"
  2179. >
  2180. <v-icon icon="mdi-alert"></v-icon>
  2181. <span class="ms-2"> 尚有欄位未填寫 </span>
  2182. </div> -->
  2183. <v-btn
  2184. v-if="step === 3"
  2185. color="purple"
  2186. variant="flat"
  2187. size="large"
  2188. @click="create()"
  2189. :loading="loading"
  2190. class="px-7"
  2191. >
  2192. 創建
  2193. </v-btn>
  2194. <div
  2195. v-if="errorField"
  2196. class="d-flex align-center text-error mt-3 position-absolute"
  2197. style="bottom: 15px"
  2198. >
  2199. <v-icon icon="mdi-alert"></v-icon>
  2200. <span class="ms-2"> 尚有欄位未填寫 </span>
  2201. </div>
  2202. <div
  2203. v-if="errorEventField"
  2204. class="d-flex align-center text-error mt-3 position-absolute"
  2205. style="bottom: 15px"
  2206. >
  2207. <v-icon icon="mdi-alert"></v-icon>
  2208. <span class="ms-2"> 尚未填寫課程時間 </span>
  2209. </div>
  2210. <v-btn
  2211. v-if="step === 4"
  2212. color="purple"
  2213. variant="outlined"
  2214. class="mb-5 mb-sm-0 me-sm-3"
  2215. >
  2216. <router-link to="/" class="px-7">回到首頁</router-link>
  2217. </v-btn>
  2218. <v-btn v-if="step === 4" color="purple" variant="flat">
  2219. <router-link to="/user/courses" class="px-7"
  2220. >前往開課專區</router-link
  2221. >
  2222. </v-btn>
  2223. </v-card-actions>
  2224. </v-card>
  2225. </v-container>
  2226. </template>
  2227. <style lang="scss">
  2228. .step-title {
  2229. text-align: center;
  2230. h5 {
  2231. font-size: 1.75em;
  2232. font-weight: 500;
  2233. letter-spacing: 0.125em;
  2234. @media (max-width: 600px) {
  2235. font-size: 1em;
  2236. }
  2237. }
  2238. p {
  2239. font-size: 0.8em;
  2240. font-weight: 400;
  2241. letter-spacing: 0.0625em;
  2242. color: #919191;
  2243. white-space: normal;
  2244. @media (max-width: 600px) {
  2245. font-size: 0.7em;
  2246. line-height: 1.5em;
  2247. }
  2248. }
  2249. }
  2250. .step-01 {
  2251. &.image-preview {
  2252. height: 17.8125em;
  2253. }
  2254. }
  2255. .step-02 {
  2256. &.image-preview {
  2257. height: 14.6875em;
  2258. }
  2259. }
  2260. .image-preview {
  2261. img {
  2262. height: 100%;
  2263. object-fit: contain;
  2264. }
  2265. }
  2266. .flex-grow-1 {
  2267. display: none !important;
  2268. }
  2269. .img-list {
  2270. .item {
  2271. height: 10em;
  2272. border-radius: 0.3125em;
  2273. background-color: #ccc; // 預設灰底
  2274. background-repeat: no-repeat;
  2275. background-position: center;
  2276. background-size: cover;
  2277. @media (max-width: 960px) {
  2278. height: 20vw;
  2279. }
  2280. }
  2281. }
  2282. .sessions-card {
  2283. border-radius: 1.875em 0.3125em 0.3125em 1.875em !important;
  2284. .v-label {
  2285. p {
  2286. // width: 5.9375em;
  2287. text-align: end;
  2288. line-height: 1.375em;
  2289. @media (max-width: 600px) {
  2290. width: 7.6875em;
  2291. }
  2292. }
  2293. }
  2294. }
  2295. // .date-item {
  2296. // display: flex;
  2297. // margin-bottom: 0.625em;
  2298. // p {
  2299. // width: 7.125em;
  2300. // display: flex;
  2301. // align-items: center;
  2302. // justify-content: end;
  2303. // white-space: nowrap;
  2304. // line-height: 1.375em;
  2305. // @media (max-width: 600px) {
  2306. // width: 7.8125em;
  2307. // }
  2308. // }
  2309. // }
  2310. .main-table {
  2311. margin: 3.125em 0;
  2312. .table-title {
  2313. background-color: var(--purple);
  2314. }
  2315. table {
  2316. thead {
  2317. border-bottom: 0.125em solid var(--purple);
  2318. tr {
  2319. &:first-child:hover {
  2320. background-color: #fff;
  2321. }
  2322. }
  2323. }
  2324. tbody {
  2325. tr {
  2326. &:hover {
  2327. background-color: #f3f3f3;
  2328. }
  2329. }
  2330. td {
  2331. cursor: pointer;
  2332. border-bottom: 0.0625em solid var(--purple);
  2333. }
  2334. }
  2335. }
  2336. }
  2337. // .main-table {
  2338. // thead {
  2339. // tr {
  2340. // &:first-child:hover {
  2341. // background-color: #fff;
  2342. // }
  2343. // }
  2344. // }
  2345. // tr {
  2346. // &:hover {
  2347. // background-color: #f3f3f3;
  2348. // }
  2349. // }
  2350. // td {
  2351. // cursor: pointer;
  2352. // }
  2353. // }
  2354. .finish-step {
  2355. line-height: 3.125em !important;
  2356. font-size: 1.375em;
  2357. text-align: center;
  2358. letter-spacing: 0.0625em;
  2359. @media (max-width: 600px) {
  2360. padding: 0;
  2361. line-height: 2em;
  2362. font-size: 1em;
  2363. }
  2364. }
  2365. .type-hint {
  2366. width: 100%;
  2367. display: block;
  2368. position: absolute;
  2369. bottom: 0.0625em;
  2370. left: 6.5625em;
  2371. color: var(--gray);
  2372. }
  2373. // .week-list {
  2374. // height: 70%;
  2375. // align-items: center;
  2376. // li {
  2377. // margin: 0.9375em 0;
  2378. // .item {
  2379. // color: var(--purple);
  2380. // padding: 0.625em;
  2381. // border-radius: 6.25em;
  2382. // border: 0.0625em solid var(--purple);
  2383. // transition: all 0.3s;
  2384. // &:hover {
  2385. // color: #fff;
  2386. // background-color: var(--purple);
  2387. // }
  2388. // }
  2389. // &.active {
  2390. // .item {
  2391. // color: #fff;
  2392. // background-color: var(--purple);
  2393. // }
  2394. // }
  2395. // }
  2396. // }
  2397. .time-item {
  2398. display: flex;
  2399. align-items: center;
  2400. p {
  2401. width: 6.0625em;
  2402. flex-wrap: nowrap;
  2403. display: flex;
  2404. white-space: nowrap;
  2405. }
  2406. }
  2407. input[type="checkbox"] {
  2408. // accent-color: var(--purple);
  2409. transform: scale(1.5);
  2410. }
  2411. .school-form {
  2412. max-width: 800px;
  2413. margin: 50px auto 0;
  2414. li {
  2415. padding: 20px;
  2416. border-bottom: 1px solid #eeeded;
  2417. font-size: 1.2em;
  2418. letter-spacing: 1px;
  2419. cursor: pointer;
  2420. &:last-child {
  2421. border-bottom: none;
  2422. }
  2423. }
  2424. .proposal-link {
  2425. padding: 25px 20px;
  2426. margin-bottom: 20px;
  2427. display: flex;
  2428. align-items: center;
  2429. color: #fff;
  2430. font-size: 1.15em;
  2431. text-shadow: 1px 1px 2px #ababab;
  2432. border-radius: 5px;
  2433. background-color: var(--blue);
  2434. transition: all 0.3s;
  2435. letter-spacing: 2px;
  2436. &:hover {
  2437. opacity: 0.8;
  2438. }
  2439. }
  2440. // width: 21.875em;
  2441. // margin: auto;
  2442. // @media (max-width: 600px) {
  2443. // width: 100%;
  2444. // }
  2445. }
  2446. .teachers-card {
  2447. cursor: pointer;
  2448. letter-spacing: 1px;
  2449. h4 {
  2450. font-size: 1.25em;
  2451. }
  2452. ul {
  2453. border-top: 1px solid #ccc;
  2454. li {
  2455. margin-bottom: 10px;
  2456. &:last-child {
  2457. margin-bottom: 0;
  2458. }
  2459. }
  2460. }
  2461. }
  2462. .v-card .v-card-title {
  2463. @media (max-width: 600px) {
  2464. white-space: normal;
  2465. line-height: 2em;
  2466. }
  2467. }
  2468. </style>