|
|
@@ -0,0 +1,744 @@
|
|
|
+<template>
|
|
|
+ <div class="kwlPage">
|
|
|
+ <div class="kwlCard">
|
|
|
+ <!-- 头部卡片 -->
|
|
|
+ <div class="kwlHeader">
|
|
|
+ <div class="kwlHeaderLeft">
|
|
|
+ <div class="kwlTitle">KWL表格</div>
|
|
|
+ <div class="kwlSubtitle">写下你已经知道的、还想知道的,学完后再回来看看自己学到了什么</div>
|
|
|
+ </div>
|
|
|
+ <div class="kwlHeaderRight" @click="showHowTo = !showHowTo">
|
|
|
+ <span>怎么做</span>
|
|
|
+ <i :class="['kwlArrow', { 'kwlArrowUp': showHowTo }]">
|
|
|
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="w-4 h-4 transition-transform "><polyline points="6 9 12 15 18 9"></polyline></svg>
|
|
|
+ </i>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 怎么做指引 -->
|
|
|
+ <div class="kwlHowTo" v-show="showHowTo">
|
|
|
+ <div class="kwlHowToItem" v-for="(step, index) in howToSteps" :key="index">
|
|
|
+ <div class="kwlStepNum">{{ index + 1 }}</div>
|
|
|
+ <div class="kwlStepText">{{ step }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 任务说明 -->
|
|
|
+ <div class="kwlCard kwlTaskDesc" v-show="work.taskDescription">
|
|
|
+ <!-- @click="showTaskDesc = !showTaskDesc" -->
|
|
|
+ <div class="kwlTaskHeader">
|
|
|
+ <span class="kwlStar">✦</span>
|
|
|
+ <span class="kwlTaskTitle">任务说明</span>
|
|
|
+ </div>
|
|
|
+ <div class="kwlTaskContent" v-show="showTaskDesc">{{ work.taskDescription }}</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- KWL 三列 -->
|
|
|
+ <div class="kwlColumns">
|
|
|
+ <!-- K - 已知 -->
|
|
|
+ <div class="kwlColumn kwlColKnow">
|
|
|
+ <div class="kwlColTitle kwlColTitleKnow">K - 已知</div>
|
|
|
+ <div class="kwlColBody">
|
|
|
+ <div class="kwlEmptyTip" v-if="!work.know || work.know.length === 0">
|
|
|
+ 点击下方添加内容
|
|
|
+ </div>
|
|
|
+ <div class="kwlItem" v-for="(item, idx) in work.know" :key="'k-' + idx">
|
|
|
+ <span class="kwlItemText">{{ item }}</span>
|
|
|
+ <span class="kwlItemDel" @click="removeItem('know', idx)">×</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="kwlAddRow">
|
|
|
+ <input
|
|
|
+ v-model="newKnow"
|
|
|
+ class="kwlInput"
|
|
|
+ placeholder="添加..."
|
|
|
+ @keyup.enter="addItem('know')"
|
|
|
+ />
|
|
|
+ <button class="kwlAddBtn kwlAddBtnKnow" @click="addItem('know')">+</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- W - 想知 -->
|
|
|
+ <div class="kwlColumn kwlColWant">
|
|
|
+ <div class="kwlColTitle kwlColTitleWant">W - 想知</div>
|
|
|
+ <div class="kwlColBody">
|
|
|
+ <div class="kwlEmptyTip" v-if="!work.want || work.want.length === 0">
|
|
|
+ 点击下方添加内容
|
|
|
+ </div>
|
|
|
+ <div class="kwlItem" v-for="(item, idx) in work.want" :key="'w-' + idx">
|
|
|
+ <span class="kwlItemText">{{ item }}</span>
|
|
|
+ <span class="kwlItemDel" @click="removeItem('want', idx)">×</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="kwlAddRow">
|
|
|
+ <input
|
|
|
+ v-model="newWant"
|
|
|
+ class="kwlInput"
|
|
|
+ placeholder="添加..."
|
|
|
+ @keyup.enter="addItem('want')"
|
|
|
+ />
|
|
|
+ <button class="kwlAddBtn kwlAddBtnWant" @click="addItem('want')">+</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- L - 已学 -->
|
|
|
+ <div class="kwlColumn kwlColLearn">
|
|
|
+ <div class="kwlColTitle kwlColTitleLearn">L - 已学</div>
|
|
|
+ <div class="kwlColBody">
|
|
|
+ <div class="kwlEmptyTip" v-if="!work.learned || work.learned.length === 0">
|
|
|
+ 点击下方添加内容
|
|
|
+ </div>
|
|
|
+ <div class="kwlItem" v-for="(item, idx) in work.learned" :key="'l-' + idx">
|
|
|
+ <span class="kwlItemText">{{ item }}</span>
|
|
|
+ <span class="kwlItemDel" @click="removeItem('learned', idx)">×</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="kwlAddRow">
|
|
|
+ <input
|
|
|
+ v-model="newLearned"
|
|
|
+ class="kwlInput"
|
|
|
+ placeholder="添加..."
|
|
|
+ @keyup.enter="addItem('learned')"
|
|
|
+ />
|
|
|
+ <button class="kwlAddBtn kwlAddBtnLearn" @click="addItem('learned')">+</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 学习反思 -->
|
|
|
+ <div class="kwlCard kwlReflection">
|
|
|
+ <div class="kwlRefTitle">学习反思</div>
|
|
|
+ <div class="kwlRefAreaWrap">
|
|
|
+ <textarea
|
|
|
+ v-model="work.reflection"
|
|
|
+ class="kwlRefTextarea"
|
|
|
+ placeholder="回顾整个学习过程,你有什么新的发现或感想?"
|
|
|
+ ></textarea>
|
|
|
+ <!-- 右下角麦克风按钮:点击开始/停止录音 -->
|
|
|
+ <div
|
|
|
+ class="kwlMicBtn"
|
|
|
+ :class="{ kwlMicBtnActive: recordObj.status == '1' }"
|
|
|
+ :title="recordObj.status == '1' ? '停止录音' : '语音输入'"
|
|
|
+ @click="recordObj.status == '1' ? stopRecord() : startRecord()"
|
|
|
+ >
|
|
|
+ <!-- 录音中:声波动画 -->
|
|
|
+ <svg v-if="recordObj.status == '1'" width="18" height="18" viewBox="0 0 120 20">
|
|
|
+ <rect x="5" y="5" width="8" :height="10" rx="3" fill="currentColor">
|
|
|
+ <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0s" />
|
|
|
+ <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0s" />
|
|
|
+ </rect>
|
|
|
+ <rect x="29" y="5" width="8" :height="10" rx="3" fill="currentColor">
|
|
|
+ <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0.2s" />
|
|
|
+ <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0.2s" />
|
|
|
+ </rect>
|
|
|
+ <rect x="53" y="5" width="8" :height="10" rx="3" fill="currentColor">
|
|
|
+ <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0.4s" />
|
|
|
+ <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0.4s" />
|
|
|
+ </rect>
|
|
|
+ <rect x="77" y="5" width="8" :height="10" rx="3" fill="currentColor">
|
|
|
+ <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0.6s" />
|
|
|
+ <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0.6s" />
|
|
|
+ </rect>
|
|
|
+ <rect x="101" y="5" width="8" :height="10" rx="3" fill="currentColor">
|
|
|
+ <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0.8s" />
|
|
|
+ <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0.8s" />
|
|
|
+ </rect>
|
|
|
+ </svg>
|
|
|
+ <!-- 空闲:麦克风图标 -->
|
|
|
+ <svg v-else viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path>
|
|
|
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2"></path>
|
|
|
+ <line x1="12" y1="19" x2="12" y2="23"></line>
|
|
|
+ <line x1="8" y1="23" x2="16" y2="23"></line>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 录音转文字 iframe -->
|
|
|
+ <iframe allow="camera *; microphone *;display-capture;midi;encrypted-media;" :src="iframeUrl" ref="iframeRef"
|
|
|
+ v-show="false"></iframe>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+export default {
|
|
|
+ props: {
|
|
|
+ workData: {
|
|
|
+ type: Object,
|
|
|
+ default: () => {
|
|
|
+ return {};
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ work: {
|
|
|
+ know: [],
|
|
|
+ want: [],
|
|
|
+ learned: [],
|
|
|
+ reflection: "",
|
|
|
+ taskDescription: "",
|
|
|
+ showHowTo: false,
|
|
|
+ kplaceholder:"",
|
|
|
+ wplaceholder:"",
|
|
|
+ lplaceholder:"",
|
|
|
+ isShowReflection: true
|
|
|
+ },
|
|
|
+ newKnow: "",
|
|
|
+ newWant: "",
|
|
|
+ newLearned: "",
|
|
|
+ showHowTo: false,
|
|
|
+ showTaskDesc: true,
|
|
|
+ recordObj: {
|
|
|
+ status: "0", // 0:默认 1:录音中
|
|
|
+ loading: false,
|
|
|
+ startTime: 0,
|
|
|
+ },
|
|
|
+ howToSteps: [
|
|
|
+ "在K栏填写:我已经知道什么(Know)",
|
|
|
+ "在W栏填写:我想知道什么(Want to know)",
|
|
|
+ "学习后在L栏填写:我学到了什么(Learned)",
|
|
|
+ "反思:对比K和L栏,总结学习收获"
|
|
|
+ ]
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ iframeUrl() {
|
|
|
+ if (this.lang && this.lang.lang == 'com') {
|
|
|
+ return `https://cloud.cocorobo.com/browser/public/index.html`;
|
|
|
+ } else if (this.lang && this.lang.lang == 'hk') {
|
|
|
+ return `https://cloud.cocorobo.hk/browser/public/index.html`;
|
|
|
+ } else {
|
|
|
+ return `https://beta.cloud.cocorobo.cn/browser/public/index.html`;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ work: {
|
|
|
+ deep: true,
|
|
|
+ handler(newValue) {
|
|
|
+ if (JSON.stringify(newValue) != JSON.stringify(this.workData.json)) {
|
|
|
+ this.changeWorkData(newValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ workData: {
|
|
|
+ handler: function (newValue, oldValue) {
|
|
|
+ let _data = JSON.parse(JSON.stringify(this.workData.json || {}));
|
|
|
+ if (JSON.stringify(newValue) != JSON.stringify(oldValue) || JSON.stringify(this.work) != JSON.stringify(_data)) {
|
|
|
+ this.work = Object.assign({
|
|
|
+ know: [],
|
|
|
+ want: [],
|
|
|
+ learned: [],
|
|
|
+ reflection: "",
|
|
|
+ taskDescription: "",
|
|
|
+ showHowTo: false,
|
|
|
+ kplaceholder:"",
|
|
|
+ wplaceholder:"",
|
|
|
+ lplaceholder:"",
|
|
|
+ isShowReflection: true
|
|
|
+ }, _data);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ deep: true,
|
|
|
+ immediate: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ changeWorkData(newValue) {
|
|
|
+ const str = JSON.stringify(newValue);
|
|
|
+ this.$emit("changeWorkData", str);
|
|
|
+ },
|
|
|
+ addItem(type) {
|
|
|
+ const inputMap = { know: "newKnow", want: "newWant", learned: "newLearned" };
|
|
|
+ const val = this[inputMap[type]];
|
|
|
+ if (!val || !val.trim()) return;
|
|
|
+ if (!this.work[type]) this.$set(this.work, type, []);
|
|
|
+ this.work[type].push(val.trim());
|
|
|
+ this[inputMap[type]] = "";
|
|
|
+ },
|
|
|
+ removeItem(type, idx) {
|
|
|
+ this.work[type].splice(idx, 1);
|
|
|
+ },
|
|
|
+ // 开始录音
|
|
|
+ startRecord() {
|
|
|
+ let iframe = this.$refs["iframeRef"];
|
|
|
+ if (!iframe || !iframe.contentWindow) {
|
|
|
+ this.$message.error("录音服务未就绪");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ iframe.contentWindow.window.document.getElementById(
|
|
|
+ "languageOptions"
|
|
|
+ ).selectedIndex = 2;
|
|
|
+
|
|
|
+ iframe.contentWindow.window.onRecognizedResult = (e) => {
|
|
|
+ let privText = e.privText;
|
|
|
+ if (!privText) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 将识别文本追加到反思区域
|
|
|
+ if (this.work.reflection) {
|
|
|
+ this.work.reflection += " " + privText;
|
|
|
+ } else {
|
|
|
+ this.work.reflection = privText;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ iframe.contentWindow.ConversationTranscriber();
|
|
|
+ this.recordObj.status = "1";
|
|
|
+ this.recordObj.startTime = new Date().getTime();
|
|
|
+ setTimeout(() => {
|
|
|
+ navigator.permissions && navigator.permissions.query({ name: 'microphone' }).then(permissionStatus => {
|
|
|
+ if (permissionStatus.state !== "granted") {
|
|
|
+ this.recordObj.status = "0";
|
|
|
+ this.recordObj.loading = false;
|
|
|
+ iframe.contentWindow.onSessionStopped = null;
|
|
|
+ iframe.contentWindow.window.onRecognizedResult = null;
|
|
|
+ this.$message.warning("未获得麦克风权限,录音已停止");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }).catch(() => {});
|
|
|
+ }, 2000);
|
|
|
+ } catch (err) {
|
|
|
+ console.error("startRecord error:", err);
|
|
|
+ this.$message.error("启动录音失败");
|
|
|
+ this.recordObj.status = "0";
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 停止录音
|
|
|
+ stopRecord() {
|
|
|
+ if (new Date().getTime() - this.recordObj.startTime < 2000) {
|
|
|
+ this.$message.info("录音时间过短");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ navigator.permissions && navigator.permissions.query({ name: 'microphone' }).then(permissionStatus => {
|
|
|
+ if (permissionStatus.state !== "granted") {
|
|
|
+ this.recordObj.status = "0";
|
|
|
+ this.recordObj.loading = false;
|
|
|
+ let iframe = this.$refs["iframeRef"];
|
|
|
+ if (iframe && iframe.contentWindow) {
|
|
|
+ iframe.contentWindow.onSessionStopped = null;
|
|
|
+ iframe.contentWindow.window.onRecognizedResult = null;
|
|
|
+ }
|
|
|
+ this.$message.warning("未获得麦克风权限");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (this.recordObj.status == '1' && !this.recordObj.loading) {
|
|
|
+ let iframe = this.$refs["iframeRef"];
|
|
|
+ if (!iframe || !iframe.contentWindow) {
|
|
|
+ this.recordObj.status = "0";
|
|
|
+ this.recordObj.loading = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.recordObj.loading = true;
|
|
|
+ try {
|
|
|
+ iframe.contentWindow.window.document
|
|
|
+ .getElementById("scenarioStopButton")
|
|
|
+ .click();
|
|
|
+ } catch (err) {
|
|
|
+ console.error("stop click error:", err);
|
|
|
+ this.recordObj.status = "0";
|
|
|
+ this.recordObj.loading = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ iframe.contentWindow.onSessionStopped = () => {
|
|
|
+ this.recordObj.status = "0";
|
|
|
+ this.recordObj.loading = false;
|
|
|
+ this.$message.success("录音已停止");
|
|
|
+ iframe.contentWindow.onSessionStopped = null;
|
|
|
+ iframe.contentWindow.window.onRecognizedResult = null;
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ this.recordObj.status = "0";
|
|
|
+ this.recordObj.loading = false;
|
|
|
+ let iframe = this.$refs["iframeRef"];
|
|
|
+ if (iframe && iframe.contentWindow) {
|
|
|
+ iframe.contentWindow.onSessionStopped = null;
|
|
|
+ iframe.contentWindow.window.onRecognizedResult = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }).catch(() => {
|
|
|
+ this.recordObj.status = "0";
|
|
|
+ this.recordObj.loading = false;
|
|
|
+ let iframe = this.$refs["iframeRef"];
|
|
|
+ if (iframe && iframe.contentWindow) {
|
|
|
+ iframe.contentWindow.onSessionStopped = null;
|
|
|
+ iframe.contentWindow.window.onRecognizedResult = null;
|
|
|
+ }
|
|
|
+ this.$message.success("录音已停止");
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {},
|
|
|
+ beforeUnmount() {}
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.kwlPage {
|
|
|
+ width: 100%;
|
|
|
+ min-height: 100%;
|
|
|
+ height: 100vh;
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 1rem;
|
|
|
+ background: #f5f7fa;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 1rem;
|
|
|
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlCard {
|
|
|
+ background: #ffffff;
|
|
|
+ border-radius: .75rem;
|
|
|
+ padding: 1.25rem 1.5rem;
|
|
|
+ box-shadow: 0 .0625rem .1875rem rgba(0, 0, 0, 0.06);
|
|
|
+}
|
|
|
+
|
|
|
+/* 头部 */
|
|
|
+.kwlHeader {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlTitle {
|
|
|
+ font-size: 1.25rem;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1a1a1a;
|
|
|
+ margin-bottom: .375rem;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlSubtitle {
|
|
|
+ font-size: .8125rem;
|
|
|
+ color: #888;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlHeaderRight {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: .375rem;
|
|
|
+ color: #3681FC;
|
|
|
+ font-size: .875rem;
|
|
|
+ cursor: pointer;
|
|
|
+ user-select: none;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlArrow {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ transition: transform 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlArrow svg {
|
|
|
+ width: 1rem;
|
|
|
+ height: 1rem;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlArrowUp {
|
|
|
+ transform: rotate(180deg);
|
|
|
+}
|
|
|
+
|
|
|
+/* 怎么做 */
|
|
|
+.kwlHowTo {
|
|
|
+ border-top: .0625rem solid #e0e0e0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: .75rem;
|
|
|
+ padding-top: .9375rem;
|
|
|
+ margin-top: .9375rem;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlHowToItem {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ gap: .75rem;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlStepNum {
|
|
|
+ width: 1.5rem;
|
|
|
+ height: 1.5rem;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #3681FC;
|
|
|
+ color: #fff;
|
|
|
+ font-size: .75rem;
|
|
|
+ font-weight: 600;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlStepText {
|
|
|
+ font-size: .875rem;
|
|
|
+ color: #333;
|
|
|
+ line-height: 1.5rem;
|
|
|
+}
|
|
|
+
|
|
|
+/* 任务说明 */
|
|
|
+.kwlTaskDesc {
|
|
|
+ padding: 1rem .9375rem;
|
|
|
+ border-left: .25rem solid #F5A623;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlTaskHeader {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: .5rem;
|
|
|
+ cursor: pointer;
|
|
|
+ user-select: none;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlStar {
|
|
|
+ color: #F5A623;
|
|
|
+ font-size: 1rem;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlTaskTitle {
|
|
|
+ font-size: .9375rem;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1a1a1a;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlTaskContent {
|
|
|
+ margin-top: .625rem;
|
|
|
+ font-size: .875rem;
|
|
|
+ color: #444;
|
|
|
+ line-height: 1.375rem;
|
|
|
+ padding-left: 1.625rem;
|
|
|
+}
|
|
|
+
|
|
|
+/* 三列 */
|
|
|
+.kwlColumns {
|
|
|
+ display: flex;
|
|
|
+ gap: 1rem;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlColumn {
|
|
|
+ flex: 1;
|
|
|
+ background: #ffffff;
|
|
|
+ border-radius: .75rem;
|
|
|
+ box-shadow: 0 .0625rem .1875rem rgba(0, 0, 0, 0.06);
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlColTitle {
|
|
|
+ font-size: .9375rem;
|
|
|
+ font-weight: 600;
|
|
|
+ padding: .875rem 1.125rem;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlColKnow {
|
|
|
+ border-top: .375rem solid #2563eb;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlColTitleKnow {
|
|
|
+ color: #2563eb;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlColWant {
|
|
|
+ border-top: .375rem solid #F56565;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlColTitleWant {
|
|
|
+ color: #F56565;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlColLearn {
|
|
|
+ border-top: .375rem solid #48BB78;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlColTitleLearn {
|
|
|
+ color: #48BB78;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlColBody {
|
|
|
+ flex: 1;
|
|
|
+ min-height: 10rem;
|
|
|
+ padding: 1rem;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: .5rem;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlEmptyTip {
|
|
|
+ text-align: center;
|
|
|
+ color: #bbb;
|
|
|
+ font-size: .8125rem;
|
|
|
+ padding: 1.875rem 0;
|
|
|
+ border: .125rem dashed #e0e0e0;
|
|
|
+ border-radius: .5rem;
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlItem {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: .5rem .75rem;
|
|
|
+ background: #f5f7fa;
|
|
|
+ border-radius: .375rem;
|
|
|
+ font-size: .8125rem;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlItemText {
|
|
|
+ flex: 1;
|
|
|
+ word-break: break-all;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlItemDel {
|
|
|
+ margin-left: .5rem;
|
|
|
+ width: 1.25rem;
|
|
|
+ height: 1.25rem;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+ color: #999;
|
|
|
+ font-size: 1rem;
|
|
|
+ line-height: 1;
|
|
|
+ transition: all 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlItemDel:hover {
|
|
|
+ background: #eee;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlAddRow {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: .5rem;
|
|
|
+ padding: .75rem 1rem;
|
|
|
+ border-top: .0625rem solid #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlInput {
|
|
|
+ flex: 1;
|
|
|
+ border: none;
|
|
|
+ outline: none;
|
|
|
+ font-size: .8125rem;
|
|
|
+ padding: .375rem 0;
|
|
|
+ background: transparent;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlInput::placeholder {
|
|
|
+ color: #bbb;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlAddBtn {
|
|
|
+ width: 2rem;
|
|
|
+ height: 2rem;
|
|
|
+ border-radius: .5rem;
|
|
|
+ border: none;
|
|
|
+ color: #fff;
|
|
|
+ font-size: 1.125rem;
|
|
|
+ font-weight: 600;
|
|
|
+ cursor: pointer;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ transition: opacity 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlAddBtn:hover {
|
|
|
+ opacity: 0.85;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlAddBtnKnow {
|
|
|
+ background: #3681FC;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlAddBtnWant {
|
|
|
+ background: #F56565;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlAddBtnLearn {
|
|
|
+ background: #48BB78;
|
|
|
+}
|
|
|
+
|
|
|
+/* 学习反思 */
|
|
|
+.kwlReflection {
|
|
|
+ padding: 1.25rem 1.5rem;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlRefTitle {
|
|
|
+ font-size: 1rem;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1a1a1a;
|
|
|
+ margin-bottom: .75rem;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlRefAreaWrap {
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlRefTextarea {
|
|
|
+ width: 100%;
|
|
|
+ min-height: 7.5rem;
|
|
|
+ padding: .75rem 3rem .75rem .875rem;
|
|
|
+ border: .0625rem solid #e0e0e0;
|
|
|
+ border-radius: .5rem;
|
|
|
+ font-size: .875rem;
|
|
|
+ line-height: 1.375rem;
|
|
|
+ resize: vertical;
|
|
|
+ outline: none;
|
|
|
+ box-sizing: border-box;
|
|
|
+ font-family: inherit;
|
|
|
+ transition: border-color 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlRefTextarea:focus {
|
|
|
+ border-color: #3681FC;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlRefTextarea::placeholder {
|
|
|
+ color: #bbb;
|
|
|
+}
|
|
|
+
|
|
|
+/* 右下角麦克风按钮 */
|
|
|
+.kwlMicBtn {
|
|
|
+ position: absolute;
|
|
|
+ right: .75rem;
|
|
|
+ bottom: .75rem;
|
|
|
+ width: 2rem;
|
|
|
+ height: 2rem;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #f5f7fa;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+ color: #888;
|
|
|
+ transition: all 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlMicBtn:hover {
|
|
|
+ background: #e8ecf1;
|
|
|
+ color: #3681FC;
|
|
|
+}
|
|
|
+
|
|
|
+/* 录音中:蓝色高亮 */
|
|
|
+.kwlMicBtnActive {
|
|
|
+ background: #3681FC;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.kwlMicBtnActive:hover {
|
|
|
+ background: #5B9BFC;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+</style>
|