@lee576/vue3-gantt 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vue3-gantt.umd.js","sources":["../node_modules/dayjs/plugin/customParseFormat.js","../node_modules/dayjs/locale/zh-cn.js","../node_modules/dayjs/locale/en.js","../node_modules/dayjs/locale/ja.js","../node_modules/dayjs/locale/ko.js","../node_modules/dayjs/locale/fr.js","../node_modules/dayjs/locale/de.js","../node_modules/dayjs/locale/es.js","../node_modules/dayjs/locale/ru.js","../src/components/gantt/Symbols.ts","../src/components/gantt/Types.ts","../src/components/gantt/LinkConfig.ts","../src/components/gantt/i18n/index.ts","../src/components/gantt/i18n/locales/zh-CN.ts","../src/components/gantt/i18n/locales/en-US.ts","../src/components/gantt/i18n/locales/ja-JP.ts","../src/components/gantt/i18n/locales/ko-KR.ts","../src/components/gantt/i18n/locales/fr-FR.ts","../src/components/gantt/i18n/locales/de-DE.ts","../src/components/gantt/i18n/locales/es-ES.ts","../src/components/gantt/i18n/locales/ru-RU.ts","../src/components/gantt/DatePicker.vue","../src/components/gantt/DatePicker.vue","../src/components/gantt/SplitPane.vue","../src/components/gantt/SplitPane.vue","../src/components/gantt/task/TaskHeader.vue","../src/components/gantt/task/TaskHeader.vue","../src/components/gantt/Store.ts","../src/components/gantt/ShareState.ts","../src/components/gantt/task/TaskRow.vue","../src/components/gantt/task/TaskRow.vue","../src/components/gantt/task/TaskRecursionRow.vue","../src/components/gantt/task/TaskRecursionRow.vue","../src/components/gantt/task/TaskContent.vue","../src/components/gantt/task/TaskContent.vue","../node_modules/dayjs/plugin/isBetween.js","../src/components/gantt/task/TaskTable.vue","../src/components/gantt/task/TaskTable.vue","../src/components/gantt/TimelineHeader.vue","../src/components/gantt/TimelineHeader.vue","../node_modules/dayjs/plugin/isoWeek.js","../src/components/gantt/Bar.vue","../src/components/gantt/Bar.vue","../src/components/gantt/BarRecursionRow.vue","../src/components/gantt/BarRecursionRow.vue","../src/components/gantt/TaskLinks.vue","../src/components/gantt/TaskLinks.vue","../src/components/gantt/TableContent.vue","../src/components/gantt/TableContent.vue","../src/components/gantt/RightTable.vue","../src/components/gantt/RightTable.vue","../src/components/gantt/themes/GanttThemes.ts","../src/components/gantt/GanttConfigPanel.vue","../src/components/gantt/GanttConfigPanel.vue","../src/components/gantt/ZodSchema.ts","../src/components/gantt/Gantt.vue","../src/components/gantt/Gantt.vue","../src/components/gantt/GanttThemeSelector.vue","../src/components/gantt/GanttThemeSelector.vue","../src/components/gantt/LanguageSelector.vue","../src/components/gantt/LanguageSelector.vue","../src/components/gantt/LinkConfigPanel.vue","../src/components/gantt/LinkConfigPanel.vue","../src/index.ts"],"sourcesContent":["!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define(t):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).dayjs_plugin_customParseFormat=t()}(this,(function(){\"use strict\";var e={LTS:\"h:mm:ss A\",LT:\"h:mm A\",L:\"MM/DD/YYYY\",LL:\"MMMM D, YYYY\",LLL:\"MMMM D, YYYY h:mm A\",LLLL:\"dddd, MMMM D, YYYY h:mm A\"},t=/(\\[[^[]*\\])|([-_:/.,()\\s]+)|(A|a|Q|YYYY|YY?|ww?|MM?M?M?|Do|DD?|hh?|HH?|mm?|ss?|S{1,3}|z|ZZ?)/g,n=/\\d/,r=/\\d\\d/,i=/\\d\\d?/,o=/\\d*[^-_:/,()\\s\\d]+/,s={},a=function(e){return(e=+e)+(e>68?1900:2e3)};var f=function(e){return function(t){this[e]=+t}},h=[/[+-]\\d\\d:?(\\d\\d)?|Z/,function(e){(this.zone||(this.zone={})).offset=function(e){if(!e)return 0;if(\"Z\"===e)return 0;var t=e.match(/([+-]|\\d\\d)/g),n=60*t[1]+(+t[2]||0);return 0===n?0:\"+\"===t[0]?-n:n}(e)}],u=function(e){var t=s[e];return t&&(t.indexOf?t:t.s.concat(t.f))},d=function(e,t){var n,r=s.meridiem;if(r){for(var i=1;i<=24;i+=1)if(e.indexOf(r(i,0,t))>-1){n=i>12;break}}else n=e===(t?\"pm\":\"PM\");return n},c={A:[o,function(e){this.afternoon=d(e,!1)}],a:[o,function(e){this.afternoon=d(e,!0)}],Q:[n,function(e){this.month=3*(e-1)+1}],S:[n,function(e){this.milliseconds=100*+e}],SS:[r,function(e){this.milliseconds=10*+e}],SSS:[/\\d{3}/,function(e){this.milliseconds=+e}],s:[i,f(\"seconds\")],ss:[i,f(\"seconds\")],m:[i,f(\"minutes\")],mm:[i,f(\"minutes\")],H:[i,f(\"hours\")],h:[i,f(\"hours\")],HH:[i,f(\"hours\")],hh:[i,f(\"hours\")],D:[i,f(\"day\")],DD:[r,f(\"day\")],Do:[o,function(e){var t=s.ordinal,n=e.match(/\\d+/);if(this.day=n[0],t)for(var r=1;r<=31;r+=1)t(r).replace(/\\[|\\]/g,\"\")===e&&(this.day=r)}],w:[i,f(\"week\")],ww:[r,f(\"week\")],M:[i,f(\"month\")],MM:[r,f(\"month\")],MMM:[o,function(e){var t=u(\"months\"),n=(u(\"monthsShort\")||t.map((function(e){return e.slice(0,3)}))).indexOf(e)+1;if(n<1)throw new Error;this.month=n%12||n}],MMMM:[o,function(e){var t=u(\"months\").indexOf(e)+1;if(t<1)throw new Error;this.month=t%12||t}],Y:[/[+-]?\\d+/,f(\"year\")],YY:[r,function(e){this.year=a(e)}],YYYY:[/\\d{4}/,f(\"year\")],Z:h,ZZ:h};function l(n){var r,i;r=n,i=s&&s.formats;for(var o=(n=r.replace(/(\\[[^\\]]+])|(LTS?|l{1,4}|L{1,4})/g,(function(t,n,r){var o=r&&r.toUpperCase();return n||i[r]||e[r]||i[o].replace(/(\\[[^\\]]+])|(MMMM|MM|DD|dddd)/g,(function(e,t,n){return t||n.slice(1)}))}))).match(t),a=o.length,f=0;f<a;f+=1){var h=o[f],u=c[h],d=u&&u[0],l=u&&u[1];o[f]=l?{regex:d,parser:l}:h.replace(/^\\[|\\]$/g,\"\")}return function(e){for(var t={},n=0,r=0;n<a;n+=1){var i=o[n];if(\"string\"==typeof i)r+=i.length;else{var s=i.regex,f=i.parser,h=e.slice(r),u=s.exec(h)[0];f.call(t,u),e=e.replace(u,\"\")}}return function(e){var t=e.afternoon;if(void 0!==t){var n=e.hours;t?n<12&&(e.hours+=12):12===n&&(e.hours=0),delete e.afternoon}}(t),t}}return function(e,t,n){n.p.customParseFormat=!0,e&&e.parseTwoDigitYear&&(a=e.parseTwoDigitYear);var r=t.prototype,i=r.parse;r.parse=function(e){var t=e.date,r=e.utc,o=e.args;this.$u=r;var a=o[1];if(\"string\"==typeof a){var f=!0===o[2],h=!0===o[3],u=f||h,d=o[2];h&&(d=o[2]),s=this.$locale(),!f&&d&&(s=n.Ls[d]),this.$d=function(e,t,n,r){try{if([\"x\",\"X\"].indexOf(t)>-1)return new Date((\"X\"===t?1e3:1)*e);var i=l(t)(e),o=i.year,s=i.month,a=i.day,f=i.hours,h=i.minutes,u=i.seconds,d=i.milliseconds,c=i.zone,m=i.week,M=new Date,Y=a||(o||s?1:M.getDate()),p=o||M.getFullYear(),v=0;o&&!s||(v=s>0?s-1:M.getMonth());var D,w=f||0,g=h||0,y=u||0,L=d||0;return c?new Date(Date.UTC(p,v,Y,w,g,y,L+60*c.offset*1e3)):n?new Date(Date.UTC(p,v,Y,w,g,y,L)):(D=new Date(p,v,Y,w,g,y,L),m&&(D=r(D).week(m).toDate()),D)}catch(e){return new Date(\"\")}}(t,a,r,n),this.init(),d&&!0!==d&&(this.$L=this.locale(d).$L),u&&t!=this.format(a)&&(this.$d=new Date(\"\")),s={}}else if(a instanceof Array)for(var c=a.length,m=1;m<=c;m+=1){o[1]=a[m-1];var M=n.apply(this,o);if(M.isValid()){this.$d=M.$d,this.$L=M.$L,this.init();break}m===c&&(this.$d=new Date(\"\"))}else i.call(this,e)}}}));","!function(e,_){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=_(require(\"dayjs\")):\"function\"==typeof define&&define.amd?define([\"dayjs\"],_):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).dayjs_locale_zh_cn=_(e.dayjs)}(this,(function(e){\"use strict\";function _(e){return e&&\"object\"==typeof e&&\"default\"in e?e:{default:e}}var t=_(e),d={name:\"zh-cn\",weekdays:\"星期日_星期一_星期二_星期三_星期四_星期五_星期六\".split(\"_\"),weekdaysShort:\"周日_周一_周二_周三_周四_周五_周六\".split(\"_\"),weekdaysMin:\"日_一_二_三_四_五_六\".split(\"_\"),months:\"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月\".split(\"_\"),monthsShort:\"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月\".split(\"_\"),ordinal:function(e,_){return\"W\"===_?e+\"周\":e+\"日\"},weekStart:1,yearStart:4,formats:{LT:\"HH:mm\",LTS:\"HH:mm:ss\",L:\"YYYY/MM/DD\",LL:\"YYYY年M月D日\",LLL:\"YYYY年M月D日Ah点mm分\",LLLL:\"YYYY年M月D日ddddAh点mm分\",l:\"YYYY/M/D\",ll:\"YYYY年M月D日\",lll:\"YYYY年M月D日 HH:mm\",llll:\"YYYY年M月D日dddd HH:mm\"},relativeTime:{future:\"%s内\",past:\"%s前\",s:\"几秒\",m:\"1 分钟\",mm:\"%d 分钟\",h:\"1 小时\",hh:\"%d 小时\",d:\"1 天\",dd:\"%d 天\",M:\"1 个月\",MM:\"%d 个月\",y:\"1 年\",yy:\"%d 年\"},meridiem:function(e,_){var t=100*e+_;return t<600?\"凌晨\":t<900?\"早上\":t<1100?\"上午\":t<1300?\"中午\":t<1800?\"下午\":\"晚上\"}};return t.default.locale(d,null,!0),d}));","!function(e,n){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=n():\"function\"==typeof define&&define.amd?define(n):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).dayjs_locale_en=n()}(this,(function(){\"use strict\";return{name:\"en\",weekdays:\"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday\".split(\"_\"),months:\"January_February_March_April_May_June_July_August_September_October_November_December\".split(\"_\"),ordinal:function(e){var n=[\"th\",\"st\",\"nd\",\"rd\"],t=e%100;return\"[\"+e+(n[(t-20)%10]||n[t]||n[0])+\"]\"}}}));","!function(e,_){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=_(require(\"dayjs\")):\"function\"==typeof define&&define.amd?define([\"dayjs\"],_):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).dayjs_locale_ja=_(e.dayjs)}(this,(function(e){\"use strict\";function _(e){return e&&\"object\"==typeof e&&\"default\"in e?e:{default:e}}var t=_(e),d={name:\"ja\",weekdays:\"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日\".split(\"_\"),weekdaysShort:\"日_月_火_水_木_金_土\".split(\"_\"),weekdaysMin:\"日_月_火_水_木_金_土\".split(\"_\"),months:\"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月\".split(\"_\"),monthsShort:\"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月\".split(\"_\"),ordinal:function(e){return e+\"日\"},formats:{LT:\"HH:mm\",LTS:\"HH:mm:ss\",L:\"YYYY/MM/DD\",LL:\"YYYY年M月D日\",LLL:\"YYYY年M月D日 HH:mm\",LLLL:\"YYYY年M月D日 dddd HH:mm\",l:\"YYYY/MM/DD\",ll:\"YYYY年M月D日\",lll:\"YYYY年M月D日 HH:mm\",llll:\"YYYY年M月D日(ddd) HH:mm\"},meridiem:function(e){return e<12?\"午前\":\"午後\"},relativeTime:{future:\"%s後\",past:\"%s前\",s:\"数秒\",m:\"1分\",mm:\"%d分\",h:\"1時間\",hh:\"%d時間\",d:\"1日\",dd:\"%d日\",M:\"1ヶ月\",MM:\"%dヶ月\",y:\"1年\",yy:\"%d年\"}};return t.default.locale(d,null,!0),d}));","!function(e,_){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=_(require(\"dayjs\")):\"function\"==typeof define&&define.amd?define([\"dayjs\"],_):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).dayjs_locale_ko=_(e.dayjs)}(this,(function(e){\"use strict\";function _(e){return e&&\"object\"==typeof e&&\"default\"in e?e:{default:e}}var d=_(e),t={name:\"ko\",weekdays:\"일요일_월요일_화요일_수요일_목요일_금요일_토요일\".split(\"_\"),weekdaysShort:\"일_월_화_수_목_금_토\".split(\"_\"),weekdaysMin:\"일_월_화_수_목_금_토\".split(\"_\"),months:\"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월\".split(\"_\"),monthsShort:\"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월\".split(\"_\"),ordinal:function(e){return e+\"일\"},formats:{LT:\"A h:mm\",LTS:\"A h:mm:ss\",L:\"YYYY.MM.DD.\",LL:\"YYYY년 MMMM D일\",LLL:\"YYYY년 MMMM D일 A h:mm\",LLLL:\"YYYY년 MMMM D일 dddd A h:mm\",l:\"YYYY.MM.DD.\",ll:\"YYYY년 MMMM D일\",lll:\"YYYY년 MMMM D일 A h:mm\",llll:\"YYYY년 MMMM D일 dddd A h:mm\"},meridiem:function(e){return e<12?\"오전\":\"오후\"},relativeTime:{future:\"%s 후\",past:\"%s 전\",s:\"몇 초\",m:\"1분\",mm:\"%d분\",h:\"한 시간\",hh:\"%d시간\",d:\"하루\",dd:\"%d일\",M:\"한 달\",MM:\"%d달\",y:\"일 년\",yy:\"%d년\"}};return d.default.locale(t,null,!0),t}));","!function(e,n){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=n(require(\"dayjs\")):\"function\"==typeof define&&define.amd?define([\"dayjs\"],n):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).dayjs_locale_fr=n(e.dayjs)}(this,(function(e){\"use strict\";function n(e){return e&&\"object\"==typeof e&&\"default\"in e?e:{default:e}}var t=n(e),i={name:\"fr\",weekdays:\"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi\".split(\"_\"),weekdaysShort:\"dim._lun._mar._mer._jeu._ven._sam.\".split(\"_\"),weekdaysMin:\"di_lu_ma_me_je_ve_sa\".split(\"_\"),months:\"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre\".split(\"_\"),monthsShort:\"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.\".split(\"_\"),weekStart:1,yearStart:4,formats:{LT:\"HH:mm\",LTS:\"HH:mm:ss\",L:\"DD/MM/YYYY\",LL:\"D MMMM YYYY\",LLL:\"D MMMM YYYY HH:mm\",LLLL:\"dddd D MMMM YYYY HH:mm\"},relativeTime:{future:\"dans %s\",past:\"il y a %s\",s:\"quelques secondes\",m:\"une minute\",mm:\"%d minutes\",h:\"une heure\",hh:\"%d heures\",d:\"un jour\",dd:\"%d jours\",M:\"un mois\",MM:\"%d mois\",y:\"un an\",yy:\"%d ans\"},ordinal:function(e){return\"\"+e+(1===e?\"er\":\"\")}};return t.default.locale(i,null,!0),i}));","!function(e,n){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=n(require(\"dayjs\")):\"function\"==typeof define&&define.amd?define([\"dayjs\"],n):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).dayjs_locale_de=n(e.dayjs)}(this,(function(e){\"use strict\";function n(e){return e&&\"object\"==typeof e&&\"default\"in e?e:{default:e}}var t=n(e),a={s:\"ein paar Sekunden\",m:[\"eine Minute\",\"einer Minute\"],mm:\"%d Minuten\",h:[\"eine Stunde\",\"einer Stunde\"],hh:\"%d Stunden\",d:[\"ein Tag\",\"einem Tag\"],dd:[\"%d Tage\",\"%d Tagen\"],M:[\"ein Monat\",\"einem Monat\"],MM:[\"%d Monate\",\"%d Monaten\"],y:[\"ein Jahr\",\"einem Jahr\"],yy:[\"%d Jahre\",\"%d Jahren\"]};function i(e,n,t){var i=a[t];return Array.isArray(i)&&(i=i[n?0:1]),i.replace(\"%d\",e)}var r={name:\"de\",weekdays:\"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag\".split(\"_\"),weekdaysShort:\"So._Mo._Di._Mi._Do._Fr._Sa.\".split(\"_\"),weekdaysMin:\"So_Mo_Di_Mi_Do_Fr_Sa\".split(\"_\"),months:\"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember\".split(\"_\"),monthsShort:\"Jan._Feb._März_Apr._Mai_Juni_Juli_Aug._Sept._Okt._Nov._Dez.\".split(\"_\"),ordinal:function(e){return e+\".\"},weekStart:1,yearStart:4,formats:{LTS:\"HH:mm:ss\",LT:\"HH:mm\",L:\"DD.MM.YYYY\",LL:\"D. MMMM YYYY\",LLL:\"D. MMMM YYYY HH:mm\",LLLL:\"dddd, D. MMMM YYYY HH:mm\"},relativeTime:{future:\"in %s\",past:\"vor %s\",s:i,m:i,mm:i,h:i,hh:i,d:i,dd:i,M:i,MM:i,y:i,yy:i}};return t.default.locale(r,null,!0),r}));","!function(e,o){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=o(require(\"dayjs\")):\"function\"==typeof define&&define.amd?define([\"dayjs\"],o):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).dayjs_locale_es=o(e.dayjs)}(this,(function(e){\"use strict\";function o(e){return e&&\"object\"==typeof e&&\"default\"in e?e:{default:e}}var s=o(e),d={name:\"es\",monthsShort:\"ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic\".split(\"_\"),weekdays:\"domingo_lunes_martes_miércoles_jueves_viernes_sábado\".split(\"_\"),weekdaysShort:\"dom._lun._mar._mié._jue._vie._sáb.\".split(\"_\"),weekdaysMin:\"do_lu_ma_mi_ju_vi_sá\".split(\"_\"),months:\"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre\".split(\"_\"),weekStart:1,formats:{LT:\"H:mm\",LTS:\"H:mm:ss\",L:\"DD/MM/YYYY\",LL:\"D [de] MMMM [de] YYYY\",LLL:\"D [de] MMMM [de] YYYY H:mm\",LLLL:\"dddd, D [de] MMMM [de] YYYY H:mm\"},relativeTime:{future:\"en %s\",past:\"hace %s\",s:\"unos segundos\",m:\"un minuto\",mm:\"%d minutos\",h:\"una hora\",hh:\"%d horas\",d:\"un día\",dd:\"%d días\",M:\"un mes\",MM:\"%d meses\",y:\"un año\",yy:\"%d años\"},ordinal:function(e){return e+\"º\"}};return s.default.locale(d,null,!0),d}));","!function(_,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=t(require(\"dayjs\")):\"function\"==typeof define&&define.amd?define([\"dayjs\"],t):(_=\"undefined\"!=typeof globalThis?globalThis:_||self).dayjs_locale_ru=t(_.dayjs)}(this,(function(_){\"use strict\";function t(_){return _&&\"object\"==typeof _&&\"default\"in _?_:{default:_}}var e=t(_),n=\"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря\".split(\"_\"),s=\"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь\".split(\"_\"),r=\"янв._февр._мар._апр._мая_июня_июля_авг._сент._окт._нояб._дек.\".split(\"_\"),o=\"янв._февр._март_апр._май_июнь_июль_авг._сент._окт._нояб._дек.\".split(\"_\"),i=/D[oD]?(\\[[^[\\]]*\\]|\\s)+MMMM?/;function d(_,t,e){var n,s;return\"m\"===e?t?\"минута\":\"минуту\":_+\" \"+(n=+_,s={mm:t?\"минута_минуты_минут\":\"минуту_минуты_минут\",hh:\"час_часа_часов\",dd:\"день_дня_дней\",MM:\"месяц_месяца_месяцев\",yy:\"год_года_лет\"}[e].split(\"_\"),n%10==1&&n%100!=11?s[0]:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?s[1]:s[2])}var u=function(_,t){return i.test(t)?n[_.month()]:s[_.month()]};u.s=s,u.f=n;var a=function(_,t){return i.test(t)?r[_.month()]:o[_.month()]};a.s=o,a.f=r;var m={name:\"ru\",weekdays:\"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота\".split(\"_\"),weekdaysShort:\"вск_пнд_втр_срд_чтв_птн_сбт\".split(\"_\"),weekdaysMin:\"вс_пн_вт_ср_чт_пт_сб\".split(\"_\"),months:u,monthsShort:a,weekStart:1,yearStart:4,formats:{LT:\"H:mm\",LTS:\"H:mm:ss\",L:\"DD.MM.YYYY\",LL:\"D MMMM YYYY г.\",LLL:\"D MMMM YYYY г., H:mm\",LLLL:\"dddd, D MMMM YYYY г., H:mm\"},relativeTime:{future:\"через %s\",past:\"%s назад\",s:\"несколько секунд\",m:d,mm:d,h:\"час\",hh:d,d:\"день\",dd:d,M:\"месяц\",MM:d,y:\"год\",yy:d},ordinal:function(_){return _},meridiem:function(_){return _<4?\"ночи\":_<12?\"утра\":_<17?\"дня\":\"вечера\"}};return e.default.locale(m,null,!0),m}));","// 定义多个 Symbol\r\nconst SetBarColorSymbol = Symbol('SetBarColor');\r\nconst AddRootTaskSymbol = Symbol('AddRootTask');\r\nconst SharedStateSymbol = Symbol('SharedState');\r\n\r\n// 以对象形式导出\r\nexport const Symbols = {\r\n SetBarColorSymbol,\r\n AddRootTaskSymbol,\r\n SharedStateSymbol\r\n};","// 连线类型枚举\r\nexport enum LinkType {\r\n FINISH_TO_START = 'finish-to-start', // 完成-开始(最常用)\r\n START_TO_START = 'start-to-start', // 开始-开始\r\n FINISH_TO_FINISH = 'finish-to-finish', // 完成-完成\r\n START_TO_FINISH = 'start-to-finish', // 开始-完成\r\n PARENT_CHILD = 'parent-child' // 父子关系\r\n}\r\n\r\n// 连线路径类型枚举\r\nexport enum LinkPathType {\r\n STRAIGHT = 'straight', // 直线\r\n BEZIER = 'bezier', // 贝塞尔曲线\r\n RIGHT_ANGLE = 'right-angle' // 直角连线\r\n}\r\n\r\n// 连线类型颜色配置\r\nexport interface LinkTypeColors {\r\n finishToStart: string; // FS - 完成-开始\r\n startToStart: string; // SS - 开始-开始\r\n finishToFinish: string; // FF - 完成-完成\r\n startToFinish: string; // SF - 开始-完成\r\n}\r\n\r\n// 连线类型显示控制\r\nexport interface LinkTypeVisibility {\r\n finishToStart: boolean; // FS - 完成-开始\r\n startToStart: boolean; // SS - 开始-开始\r\n finishToFinish: boolean; // FF - 完成-完成\r\n startToFinish: boolean; // SF - 开始-完成\r\n parentChild: boolean; // 父子关系\r\n}\r\n\r\n// 基础连线配置接口\r\nexport interface LinkConfig {\r\n color: string;\r\n width: number;\r\n dashArray?: string;\r\n showArrow: boolean;\r\n arrowColor?: string;\r\n arrowSize: number;\r\n showLabels: boolean;\r\n labelColor: string;\r\n labelFontSize: number;\r\n cornerRadius: number;\r\n pathType: LinkPathType;\r\n bezierCurvature: number;\r\n rightAngleOffset: number;\r\n smoothCorners: boolean;\r\n enableDashAnimation: boolean;\r\n dashAnimationSpeed: number;\r\n parentChildStyle: {\r\n color: string;\r\n width: number;\r\n dashArray?: string;\r\n };\r\n linkTypeColors?: LinkTypeColors;\r\n linkTypeVisibility?: LinkTypeVisibility;\r\n}\r\n\r\n// 连线数据接口\r\nexport interface TaskLink {\r\n id: string;\r\n sourceId: string;\r\n targetId: string;\r\n type: LinkType;\r\n label?: string;\r\n path: string;\r\n arrowPoints: string;\r\n labelX: number;\r\n labelY: number;\r\n}\r\n\r\n// 任务依赖关系接口\r\nexport interface TaskDependency {\r\n id: string;\r\n sourceTaskId: string;\r\n targetTaskId: string;\r\n type: LinkType;\r\n lag?: number; // 延迟天数\r\n label?: string;\r\n}\r\n\r\n// 样式配置接口\r\nexport interface StyleConfig {\r\n headersHeight: number;\r\n rowHeight: number;\r\n setBarColor: (row: Record<string, any>) => string;\r\n}\r\n\r\n// 数据配置接口\r\nexport interface DataConfig {\r\n dataSource: any[];\r\n taskHeaders: any[];\r\n mapFields: Record<string, any>;\r\n queryStartDate: string;\r\n queryEndDate: string;\r\n dependencies?: Omit<TaskDependency, 'id'>[];\r\n}\r\n\r\n// 进度更新事件详情接口\r\nexport interface ProgressUpdateDetail {\r\n taskId: any;\r\n oldProgress: number;\r\n newProgress: number;\r\n task: Record<string, any>;\r\n}\r\n\r\n// 事件配置接口\r\nexport interface EventConfig {\r\n addRootTask: (row: Record<string, any> | null) => void;\r\n addSubTask: (task: any) => void;\r\n removeTask: (task: any) => void;\r\n editTask: (task: any) => void;\r\n queryTask: (startDate: string, endDate: string, mode: string) => void;\r\n barDate: (id: any, startDate: string, endDate: string) => void;\r\n allowChangeTaskDate: (allow: boolean) => void;\r\n updateProgress?: (detail: ProgressUpdateDetail) => void;\r\n}\r\n\r\n// 任务表头接口\r\nexport interface TaskHeader {\r\n title: string;\r\n key: string;\r\n width?: number;\r\n align?: 'left' | 'center' | 'right';\r\n}","import { reactive } from 'vue';\r\nimport { LinkType, LinkPathType, type LinkConfig, type TaskDependency } from './Types';\r\n\r\n// 重新导出类型,方便其他组件使用\r\nexport { LinkType, LinkPathType } from './Types';\r\n\r\n// 预定义的连线样式主题\r\nexport const LinkThemes = {\r\n // 默认主题\r\n default: {\r\n color: '#3498db',\r\n width: 2,\r\n dashArray: undefined,\r\n showArrow: true,\r\n arrowColor: undefined,\r\n arrowSize: 8,\r\n showLabels: false,\r\n labelColor: '#666',\r\n labelFontSize: 12,\r\n cornerRadius: 5,\r\n pathType: LinkPathType.BEZIER,\r\n bezierCurvature: 0.4,\r\n rightAngleOffset: 30,\r\n smoothCorners: true,\r\n enableDashAnimation: true,\r\n dashAnimationSpeed: 0.8,\r\n parentChildStyle: {\r\n color: '#95a5a6',\r\n width: 1,\r\n dashArray: '3,3'\r\n },\r\n // 各类型连线颜色\r\n linkTypeColors: {\r\n finishToStart: '#3498db', // 蓝色 - FS\r\n startToStart: '#2ecc71', // 绿色 - SS\r\n finishToFinish: '#e74c3c', // 红色 - FF\r\n startToFinish: '#f39c12' // 橙色 - SF\r\n },\r\n // 各类型连线显示控制\r\n linkTypeVisibility: {\r\n finishToStart: true,\r\n startToStart: true,\r\n finishToFinish: true,\r\n startToFinish: true,\r\n parentChild: true\r\n }\r\n },\r\n \r\n // 商务主题\r\n business: {\r\n color: '#2c3e50',\r\n width: 2,\r\n dashArray: undefined,\r\n showArrow: true,\r\n arrowColor: '#e74c3c',\r\n arrowSize: 10,\r\n showLabels: true,\r\n labelColor: '#34495e',\r\n labelFontSize: 11,\r\n cornerRadius: 8,\r\n pathType: LinkPathType.RIGHT_ANGLE,\r\n bezierCurvature: 0.3,\r\n rightAngleOffset: 40,\r\n smoothCorners: true,\r\n enableDashAnimation: true,\r\n dashAnimationSpeed: 0.6,\r\n parentChildStyle: {\r\n color: '#7f8c8d',\r\n width: 2,\r\n dashArray: '5,2'\r\n },\r\n linkTypeColors: {\r\n finishToStart: '#2c3e50',\r\n startToStart: '#27ae60',\r\n finishToFinish: '#c0392b',\r\n startToFinish: '#d35400'\r\n },\r\n linkTypeVisibility: {\r\n finishToStart: true,\r\n startToStart: true,\r\n finishToFinish: true,\r\n startToFinish: true,\r\n parentChild: true\r\n }\r\n },\r\n \r\n // 现代主题\r\n modern: {\r\n color: '#00bcd4',\r\n width: 3,\r\n dashArray: undefined,\r\n showArrow: true,\r\n arrowColor: '#ff5722',\r\n arrowSize: 12,\r\n showLabels: false,\r\n labelColor: '#607d8b',\r\n labelFontSize: 12,\r\n cornerRadius: 10,\r\n pathType: LinkPathType.BEZIER,\r\n bezierCurvature: 0.6,\r\n rightAngleOffset: 50,\r\n smoothCorners: true,\r\n enableDashAnimation: true,\r\n dashAnimationSpeed: 1,\r\n parentChildStyle: {\r\n color: '#90a4ae',\r\n width: 2,\r\n dashArray: '8,4'\r\n },\r\n linkTypeColors: {\r\n finishToStart: '#00bcd4',\r\n startToStart: '#4caf50',\r\n finishToFinish: '#f44336',\r\n startToFinish: '#ff9800'\r\n },\r\n linkTypeVisibility: {\r\n finishToStart: true,\r\n startToStart: true,\r\n finishToFinish: true,\r\n startToFinish: true,\r\n parentChild: true\r\n }\r\n },\r\n \r\n // 简约主题\r\n minimal: {\r\n color: '#666',\r\n width: 1,\r\n dashArray: undefined,\r\n showArrow: false,\r\n arrowColor: undefined,\r\n arrowSize: 6,\r\n showLabels: false,\r\n labelColor: '#999',\r\n labelFontSize: 10,\r\n cornerRadius: 3,\r\n pathType: LinkPathType.STRAIGHT,\r\n bezierCurvature: 0.2,\r\n rightAngleOffset: 20,\r\n smoothCorners: false,\r\n enableDashAnimation: false,\r\n dashAnimationSpeed: 0.8,\r\n parentChildStyle: {\r\n color: '#ccc',\r\n width: 1,\r\n dashArray: '2,2'\r\n },\r\n linkTypeColors: {\r\n finishToStart: '#666',\r\n startToStart: '#888',\r\n finishToFinish: '#555',\r\n startToFinish: '#777'\r\n },\r\n linkTypeVisibility: {\r\n finishToStart: true,\r\n startToStart: true,\r\n finishToFinish: true,\r\n startToFinish: true,\r\n parentChild: true\r\n }\r\n },\r\n \r\n // 彩色主题\r\n colorful: {\r\n color: '#9c27b0',\r\n width: 2,\r\n dashArray: undefined,\r\n showArrow: true,\r\n arrowColor: '#ff9800',\r\n arrowSize: 9,\r\n showLabels: true,\r\n labelColor: '#673ab7',\r\n labelFontSize: 11,\r\n cornerRadius: 6,\r\n pathType: LinkPathType.RIGHT_ANGLE,\r\n bezierCurvature: 0.5,\r\n rightAngleOffset: 35,\r\n smoothCorners: true,\r\n enableDashAnimation: true,\r\n dashAnimationSpeed: 1,\r\n parentChildStyle: {\r\n color: '#4caf50',\r\n width: 2,\r\n dashArray: '4,3'\r\n },\r\n linkTypeColors: {\r\n finishToStart: '#9c27b0',\r\n startToStart: '#4caf50',\r\n finishToFinish: '#e91e63',\r\n startToFinish: '#ff9800'\r\n },\r\n linkTypeVisibility: {\r\n finishToStart: true,\r\n startToStart: true,\r\n finishToFinish: true,\r\n startToFinish: true,\r\n parentChild: true\r\n }\r\n }\r\n} as const;\r\n\r\n// 连线类型配置\r\nexport const LinkTypeConfig = {\r\n [LinkType.FINISH_TO_START]: {\r\n name: '完成-开始',\r\n description: '前置任务完成后,后续任务才能开始',\r\n color: '#3498db',\r\n priority: 1\r\n },\r\n [LinkType.START_TO_START]: {\r\n name: '开始-开始',\r\n description: '两个任务同时开始',\r\n color: '#2ecc71',\r\n priority: 2\r\n },\r\n [LinkType.FINISH_TO_FINISH]: {\r\n name: '完成-完成',\r\n description: '两个任务同时完成',\r\n color: '#e74c3c',\r\n priority: 3\r\n },\r\n [LinkType.START_TO_FINISH]: {\r\n name: '开始-完成',\r\n description: '前置任务开始后,后续任务才能完成',\r\n color: '#f39c12',\r\n priority: 4\r\n },\r\n [LinkType.PARENT_CHILD]: {\r\n name: '父子关系',\r\n description: '显示任务的层级关系',\r\n color: '#95a5a6',\r\n priority: 0\r\n }\r\n} as const;\r\n\r\n// 全局连线配置管理器\r\nexport class LinkConfigManager {\r\n private config = reactive<LinkConfig>({ ...LinkThemes.default });\r\n private readonly STORAGE_KEY = 'gantt-link-config';\r\n \r\n constructor() {\r\n this.loadFromStorage();\r\n }\r\n \r\n // 从 localStorage 加载配置\r\n private loadFromStorage(): void {\r\n try {\r\n const stored = localStorage.getItem(this.STORAGE_KEY);\r\n if (stored) {\r\n const parsedConfig = JSON.parse(stored);\r\n Object.assign(this.config, parsedConfig);\r\n }\r\n } catch (error) {\r\n console.warn('加载连线配置失败,使用默认配置:', error);\r\n }\r\n }\r\n \r\n // 保存配置到 localStorage\r\n private saveToStorage(): void {\r\n try {\r\n localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.config));\r\n } catch (error) {\r\n console.warn('保存连线配置失败:', error);\r\n }\r\n }\r\n \r\n // 获取当前配置\r\n getConfig(): LinkConfig {\r\n return this.config;\r\n }\r\n \r\n // 设置主题\r\n setTheme(themeName: keyof typeof LinkThemes): void {\r\n Object.assign(this.config, LinkThemes[themeName]);\r\n this.saveToStorage();\r\n }\r\n \r\n // 更新配置\r\n updateConfig(newConfig: Partial<LinkConfig>): void {\r\n Object.assign(this.config, newConfig);\r\n this.saveToStorage();\r\n }\r\n \r\n // 重置为默认配置\r\n reset(): void {\r\n Object.assign(this.config, LinkThemes.default);\r\n this.saveToStorage();\r\n }\r\n \r\n // 获取特定类型连线的样式\r\n getLinkStyle(linkType: LinkType): Partial<LinkConfig> {\r\n const typeConfig = LinkTypeConfig[linkType];\r\n \r\n if (linkType === LinkType.PARENT_CHILD) {\r\n return {\r\n color: this.config.parentChildStyle.color,\r\n width: this.config.parentChildStyle.width,\r\n dashArray: this.config.parentChildStyle.dashArray\r\n };\r\n }\r\n \r\n return {\r\n color: typeConfig.color,\r\n width: this.config.width,\r\n dashArray: this.config.dashArray\r\n };\r\n }\r\n \r\n // 导出配置\r\n exportConfig(): string {\r\n return JSON.stringify(this.config, null, 2);\r\n }\r\n \r\n // 导入配置\r\n importConfig(configJson: string): boolean {\r\n try {\r\n const importedConfig = JSON.parse(configJson);\r\n this.updateConfig(importedConfig);\r\n return true;\r\n } catch (error) {\r\n console.error('导入配置失败:', error);\r\n return false;\r\n }\r\n }\r\n \r\n // 清除存储的配置\r\n clearStorage(): void {\r\n try {\r\n localStorage.removeItem(this.STORAGE_KEY);\r\n this.reset();\r\n } catch (error) {\r\n console.warn('清除配置失败:', error);\r\n }\r\n }\r\n}\r\n\r\n// 创建全局实例\r\nexport const linkConfigManager = new LinkConfigManager();\r\n\r\n// 连线配置组合式函数\r\nexport function useLinkConfig() {\r\n return {\r\n config: linkConfigManager.getConfig(),\r\n setTheme: linkConfigManager.setTheme.bind(linkConfigManager),\r\n updateConfig: linkConfigManager.updateConfig.bind(linkConfigManager),\r\n reset: linkConfigManager.reset.bind(linkConfigManager),\r\n getLinkStyle: linkConfigManager.getLinkStyle.bind(linkConfigManager),\r\n exportConfig: linkConfigManager.exportConfig.bind(linkConfigManager),\r\n importConfig: linkConfigManager.importConfig.bind(linkConfigManager),\r\n clearStorage: linkConfigManager.clearStorage.bind(linkConfigManager),\r\n themes: LinkThemes,\r\n linkTypes: LinkTypeConfig\r\n };\r\n}\r\n\r\n// 连线数据管理\r\nexport class LinkDataManager {\r\n private dependencies = reactive<TaskDependency[]>([]);\r\n private readonly STORAGE_KEY = 'gantt-link-dependencies';\r\n \r\n constructor() {\r\n this.loadFromStorage();\r\n }\r\n \r\n // 从 localStorage 加载依赖关系\r\n private loadFromStorage(): void {\r\n try {\r\n const stored = localStorage.getItem(this.STORAGE_KEY);\r\n if (stored) {\r\n const parsedDependencies = JSON.parse(stored);\r\n this.dependencies.splice(0, this.dependencies.length, ...parsedDependencies);\r\n }\r\n } catch (error) {\r\n console.warn('加载连线依赖关系失败:', error);\r\n }\r\n }\r\n \r\n // 保存依赖关系到 localStorage\r\n private saveToStorage(): void {\r\n try {\r\n localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.dependencies));\r\n } catch (error) {\r\n console.warn('保存连线依赖关系失败:', error);\r\n }\r\n }\r\n \r\n // 添加依赖关系\r\n addDependency(dependency: Omit<TaskDependency, 'id'>): string {\r\n const id = `link-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\r\n this.dependencies.push({ ...dependency, id });\r\n this.saveToStorage();\r\n return id;\r\n }\r\n \r\n // 删除依赖关系\r\n removeDependency(id: string): boolean {\r\n const index = this.dependencies.findIndex(dep => dep.id === id);\r\n if (index > -1) {\r\n this.dependencies.splice(index, 1);\r\n this.saveToStorage();\r\n return true;\r\n }\r\n return false;\r\n }\r\n \r\n // 获取所有依赖关系\r\n getDependencies(): TaskDependency[] {\r\n return this.dependencies;\r\n }\r\n \r\n // 获取任务的依赖关系\r\n getTaskDependencies(taskId: string): TaskDependency[] {\r\n return this.dependencies.filter(dep => \r\n dep.sourceTaskId === taskId || dep.targetTaskId === taskId\r\n );\r\n }\r\n \r\n // 清空所有依赖关系\r\n clear(): void {\r\n this.dependencies.splice(0);\r\n this.saveToStorage();\r\n }\r\n \r\n // 清除存储的依赖关系\r\n clearStorage(): void {\r\n try {\r\n localStorage.removeItem(this.STORAGE_KEY);\r\n this.clear();\r\n } catch (error) {\r\n console.warn('清除依赖关系失败:', error);\r\n }\r\n }\r\n \r\n // 检查是否会形成循环依赖\r\n wouldCreateCycle(sourceId: string, targetId: string): boolean {\r\n const visited = new Set<string>();\r\n const recursionStack = new Set<string>();\r\n \r\n const hasCycle = (nodeId: string): boolean => {\r\n if (recursionStack.has(nodeId)) return true;\r\n if (visited.has(nodeId)) return false;\r\n \r\n visited.add(nodeId);\r\n recursionStack.add(nodeId);\r\n \r\n const dependencies = this.dependencies.filter(dep => dep.sourceTaskId === nodeId);\r\n for (const dep of dependencies) {\r\n if (hasCycle(dep.targetTaskId)) return true;\r\n }\r\n \r\n recursionStack.delete(nodeId);\r\n return false;\r\n };\r\n \r\n // 临时添加新的依赖关系进行检查\r\n this.dependencies.push({\r\n id: 'temp',\r\n sourceTaskId: sourceId,\r\n targetTaskId: targetId,\r\n type: LinkType.FINISH_TO_START\r\n });\r\n \r\n const result = hasCycle(sourceId);\r\n \r\n // 移除临时依赖关系\r\n this.dependencies.pop();\r\n \r\n return result;\r\n }\r\n}\r\n\r\n// 创建全局实例\r\nexport const linkDataManager = new LinkDataManager();\r\n\r\n// 连线数据组合式函数\r\nexport function useLinkData() {\r\n return {\r\n dependencies: linkDataManager.getDependencies(),\r\n addDependency: linkDataManager.addDependency.bind(linkDataManager),\r\n removeDependency: linkDataManager.removeDependency.bind(linkDataManager),\r\n getTaskDependencies: linkDataManager.getTaskDependencies.bind(linkDataManager),\r\n clear: linkDataManager.clear.bind(linkDataManager),\r\n clearStorage: linkDataManager.clearStorage.bind(linkDataManager),\r\n wouldCreateCycle: linkDataManager.wouldCreateCycle.bind(linkDataManager)\r\n };\r\n}","/**\n * 甘特图国际化系统\n * 支持中文、英文等多语言切换\n */\n\nimport { ref, computed } from 'vue';\nimport zhCN from './locales/zh-CN';\nimport enUS from './locales/en-US';\nimport jaJP from './locales/ja-JP';\nimport koKR from './locales/ko-KR';\nimport frFR from './locales/fr-FR';\nimport deDE from './locales/de-DE';\nimport esES from './locales/es-ES';\nimport ruRU from './locales/ru-RU';\n\n// 支持的语言类型\nexport type Locale = 'zh-CN' | 'en-US' | 'ja-JP' | 'ko-KR' | 'fr-FR' | 'de-DE' | 'es-ES' | 'ru-RU';\n\n// 语言包类型\nexport type Messages = typeof zhCN;\n\n// 所有语言包\nconst messages: Record<Locale, Messages> = {\n 'zh-CN': zhCN,\n 'en-US': enUS,\n 'ja-JP': jaJP,\n 'ko-KR': koKR,\n 'fr-FR': frFR,\n 'de-DE': deDE,\n 'es-ES': esES,\n 'ru-RU': ruRU\n};\n\n// 当前语言\nconst currentLocale = ref<Locale>('zh-CN');\n\n// 从localStorage读取保存的语言设置\nconst savedLocale = localStorage.getItem('gantt-locale') as Locale;\nif (savedLocale && messages[savedLocale]) {\n currentLocale.value = savedLocale;\n}\n\n/**\n * 获取翻译文本\n * @param key 翻译键,支持点号路径如 'common.confirm'\n * @returns 翻译后的文本\n */\nexport function t(key: string): string {\n const keys = key.split('.');\n let value: any = messages[currentLocale.value];\n \n for (const k of keys) {\n if (value && typeof value === 'object') {\n value = value[k];\n } else {\n return key; // 如果找不到,返回原key\n }\n }\n \n return typeof value === 'string' ? value : key;\n}\n\n/**\n * 设置当前语言\n * @param locale 语言代码\n */\nexport function setLocale(locale: Locale) {\n if (messages[locale]) {\n currentLocale.value = locale;\n localStorage.setItem('gantt-locale', locale);\n }\n}\n\n/**\n * 获取当前语言\n */\nexport function getLocale(): Locale {\n return currentLocale.value;\n}\n\n/**\n * 获取所有支持的语言\n */\nexport function getLocales(): { value: Locale; label: string }[] {\n return [\n { value: 'zh-CN', label: '🇨🇳 简体中文' },\n { value: 'en-US', label: '🇺🇸 English' },\n { value: 'ja-JP', label: '🇯🇵 日本語' },\n { value: 'ko-KR', label: '🇰🇷 한국어' },\n { value: 'fr-FR', label: '🇫🇷 Français' },\n { value: 'de-DE', label: '🇩🇪 Deutsch' },\n { value: 'es-ES', label: '🇪🇸 Español' },\n { value: 'ru-RU', label: '🇷🇺 Русский' }\n ];\n}\n\n/**\n * 创建响应式i18n hook\n */\nexport function useI18n() {\n const locale = computed(() => currentLocale.value);\n \n return {\n locale,\n t,\n setLocale,\n getLocale,\n getLocales\n };\n}\n\nexport default {\n t,\n setLocale,\n getLocale,\n getLocales,\n useI18n\n};\n","/**\n * 简体中文语言包\n */\nexport default {\n // 通用\n common: {\n confirm: '确定',\n cancel: '取消',\n save: '保存',\n delete: '删除',\n edit: '编辑',\n add: '添加',\n close: '关闭',\n export: '导出',\n import: '导入',\n config: '配置',\n settings: '设置',\n theme: '主题',\n to: '至',\n selectDate: '请选择日期'\n },\n \n // 日期时间\n date: {\n year: '年',\n month: '月',\n day: '日',\n hour: '时',\n week: '周',\n today: '今天',\n monday: '一',\n tuesday: '二',\n wednesday: '三',\n thursday: '四',\n friday: '五',\n saturday: '六',\n sunday: '日',\n january: '一月',\n february: '二月',\n march: '三月',\n april: '四月',\n may: '五月',\n june: '六月',\n july: '七月',\n august: '八月',\n september: '九月',\n october: '十月',\n november: '十一月',\n december: '十二月'\n },\n \n // 日期格式\n dateFormat: {\n full: 'YYYY年MM月DD日',\n short: 'YYYY-MM-DD',\n monthDay: 'MM月DD日',\n yearMonth: 'YYYY年MM月'\n },\n \n // 视图模式\n viewMode: {\n month: '月',\n week: '周',\n day: '日',\n hour: '时'\n },\n \n // 任务\n task: {\n addRoot: '添加根任务',\n addSub: '添加子任务',\n remove: '删除任务',\n edit: '编辑任务',\n name: '任务名称',\n priority: '优先级',\n startDate: '开始时间',\n endDate: '结束时间',\n duration: '耗时',\n progress: '进度',\n serialNumber: '序号'\n },\n \n // 连线图例\n link: {\n legend: '连线图例',\n parentChild: '父子关系',\n finishToStart: '完成-开始',\n startToStart: '开始-开始',\n finishToFinish: '完成-完成',\n startToFinish: '开始-完成',\n fs: 'FS',\n ss: 'SS',\n ff: 'FF',\n sf: 'SF',\n pc: 'PC'\n },\n \n // 配置面板\n configPanel: {\n title: '甘特图配置',\n themeSettings: '主题设置',\n linkSettings: '连线设置',\n languageSettings: '语言设置',\n currentTheme: '当前',\n previewTheme: '预览',\n exportConfig: '导出配置',\n importConfig: '导入配置',\n \n // 连线配置\n linkConfig: {\n info: '以下配置用于任务依赖连线(完成-开始、开始-开始等关系)',\n pathType: '路径类型',\n straight: '直线',\n bezier: '贝塞尔',\n rightAngle: '直角',\n color: '颜色',\n width: '线宽',\n dashStyle: '虚线样式',\n solid: '实线',\n shortDash: '短虚线',\n mediumDash: '中虚线',\n longDash: '长虚线',\n dotDash: '点划线',\n curvature: '弯曲度',\n offset: '偏移距离',\n smoothCorners: '平滑转角',\n cornerRadius: '转角半径',\n showArrow: '显示箭头',\n arrowSize: '箭头大小',\n arrowColor: '箭头颜色',\n syncColor: '同步',\n dashAnimation: '虚线流动动画',\n animationSpeed: '动画速度',\n showLabels: '显示标签',\n labelColor: '标签颜色',\n fontSize: '字体大小',\n typeColors: '依赖类型颜色',\n parentChildStyle: '父子关系连线样式',\n parentChildInfo: '用于显示父任务与子任务之间的层级结构关系'\n }\n },\n \n // 主题\n theme: {\n metro: 'Metro 金属质感',\n light: '浅色主题',\n dark: '深色主题',\n colorful: '多彩主题',\n ocean: '海洋主题',\n apple: 'Apple 苹果风格',\n classic: '经典商务',\n metroDesc: '经典 Windows Metro 风格',\n lightDesc: '清爽明亮的浅色风格',\n darkDesc: '优雅专业的深色风格',\n colorfulDesc: '活力四射的多彩风格',\n oceanDesc: '沉静舒适的海洋风格',\n appleDesc: '简约优雅的 macOS 风格',\n classicDesc: '传统稳重的商务风格'\n },\n \n // 日期选择器\n datePicker: {\n selectDate: '请选择日期',\n clearDate: '清除日期'\n },\n \n // 提示信息\n tooltip: {\n addSubTask: '添加子任务',\n removeTask: '删除当前任务',\n syncArrowColor: '与线条颜色同步',\n close: '关闭'\n }\n};\n","/**\r\n * English language pack\r\n */\r\nexport default {\r\n // Common\r\n common: {\r\n confirm: 'Confirm',\r\n cancel: 'Cancel',\r\n save: 'Save',\r\n delete: 'Delete',\r\n edit: 'Edit',\r\n add: 'Add',\r\n close: 'Close',\r\n export: 'Export',\r\n import: 'Import',\r\n config: 'Config',\r\n settings: 'Settings',\r\n theme: 'Theme',\r\n to: 'to',\r\n selectDate: 'Select Date'\r\n },\r\n \r\n // Date & Time\r\n date: {\r\n year: 'Year',\r\n month: 'Month',\r\n day: 'Day',\r\n hour: 'Hour',\r\n week: 'Week',\r\n today: 'Today',\r\n monday: 'Mon',\r\n tuesday: 'Tue',\r\n wednesday: 'Wed',\r\n thursday: 'Thu',\r\n friday: 'Fri',\r\n saturday: 'Sat',\r\n sunday: 'Sun',\r\n january: 'January',\r\n february: 'February',\r\n march: 'March',\r\n april: 'April',\r\n may: 'May',\r\n june: 'June',\r\n july: 'July',\r\n august: 'August',\r\n september: 'September',\r\n october: 'October',\r\n november: 'November',\r\n december: 'December'\r\n },\r\n \r\n // Date Format\r\n dateFormat: {\r\n full: 'MMMM DD, YYYY',\r\n short: 'MM/DD/YYYY',\r\n monthDay: 'MMM DD',\r\n yearMonth: 'MMMM YYYY'\r\n },\r\n \r\n // View Mode\r\n viewMode: {\r\n month: 'Month',\r\n week: 'Week',\r\n day: 'Day',\r\n hour: 'Hour'\r\n },\r\n \r\n // Task\r\n task: {\r\n addRoot: 'Add Root Task',\r\n addSub: 'Add Sub Task',\r\n remove: 'Delete Task',\r\n edit: 'Edit Task',\r\n name: 'Task Name',\r\n priority: 'Priority',\r\n startDate: 'Start Date',\r\n endDate: 'End Date',\r\n duration: 'Duration',\r\n progress: 'Progress',\r\n serialNumber: 'No.'\r\n },\r\n \r\n // Link Legend\r\n link: {\r\n legend: 'Link Legend',\r\n parentChild: 'Parent-Child',\r\n finishToStart: 'Finish-to-Start',\r\n startToStart: 'Start-to-Start',\r\n finishToFinish: 'Finish-to-Finish',\r\n startToFinish: 'Start-to-Finish',\r\n fs: 'FS',\r\n ss: 'SS',\r\n ff: 'FF',\r\n sf: 'SF',\r\n pc: 'PC'\r\n },\r\n \r\n // Config Panel\r\n configPanel: {\r\n title: 'Gantt Configuration',\r\n themeSettings: 'Theme Settings',\r\n linkSettings: 'Link Settings',\r\n languageSettings: 'Language Settings',\r\n currentTheme: 'Current',\r\n previewTheme: 'Preview',\r\n exportConfig: 'Export Config',\r\n importConfig: 'Import Config',\r\n \r\n // Link Configuration\r\n linkConfig: {\r\n info: 'Configure task dependency links (Finish-to-Start, Start-to-Start, etc.)',\r\n pathType: 'Path Type',\r\n straight: 'Straight',\r\n bezier: 'Bezier',\r\n rightAngle: 'Right Angle',\r\n color: 'Color',\r\n width: 'Line Width',\r\n dashStyle: 'Dash Style',\r\n solid: 'Solid',\r\n shortDash: 'Short Dash',\r\n mediumDash: 'Medium Dash',\r\n longDash: 'Long Dash',\r\n dotDash: 'Dot Dash',\r\n curvature: 'Curvature',\r\n offset: 'Offset Distance',\r\n smoothCorners: 'Smooth Corners',\r\n cornerRadius: 'Corner Radius',\r\n showArrow: 'Show Arrow',\r\n arrowSize: 'Arrow Size',\r\n arrowColor: 'Arrow Color',\r\n syncColor: 'Sync',\r\n dashAnimation: 'Dash Animation',\r\n animationSpeed: 'Animation Speed',\r\n showLabels: 'Show Labels',\r\n labelColor: 'Label Color',\r\n fontSize: 'Font Size',\r\n typeColors: 'Dependency Type Colors',\r\n parentChildStyle: 'Parent-Child Link Style',\r\n parentChildInfo: 'Display hierarchical relationship between parent and child tasks'\r\n }\r\n },\r\n \r\n // Theme\r\n theme: {\r\n metro: 'Metro',\r\n light: 'Light',\r\n dark: 'Dark',\r\n colorful: 'Colorful',\r\n ocean: 'Ocean',\r\n apple: 'Apple',\r\n classic: 'Classic',\r\n metroDesc: 'Classic Windows Metro style',\r\n lightDesc: 'Fresh and bright light style',\r\n darkDesc: 'Elegant and professional dark style',\r\n colorfulDesc: 'Vibrant and colorful style',\r\n oceanDesc: 'Calm and comfortable ocean style',\r\n appleDesc: 'Minimalist and elegant macOS style',\r\n classicDesc: 'Traditional and stable business style'\r\n },\r\n \r\n // Date Picker\r\n datePicker: {\r\n selectDate: 'Select Date',\r\n clearDate: 'Clear Date'\r\n },\r\n \r\n // Tooltip\r\n tooltip: {\r\n addSubTask: 'Add Sub Task',\r\n removeTask: 'Delete Current Task',\r\n syncArrowColor: 'Sync with line color',\r\n close: 'Close'\r\n }\r\n};\r\n","/**\r\n * Japanese language pack\r\n * 日本語言語パック\r\n */\r\nexport default {\r\n // Common\r\n common: {\r\n confirm: '確認',\r\n cancel: 'キャンセル',\r\n save: '保存',\r\n delete: '削除',\r\n edit: '編集',\r\n add: '追加',\r\n close: '閉じる',\r\n export: 'エクスポート',\r\n import: 'インポート',\r\n config: '設定',\r\n settings: '設定',\r\n theme: 'テーマ',\r\n to: 'から',\r\n selectDate: '日付を選択'\r\n },\r\n \r\n // Date & Time\r\n date: {\r\n year: '年',\r\n month: '月',\r\n day: '日',\r\n hour: '時',\r\n week: '週',\r\n today: '今日',\r\n monday: '月',\r\n tuesday: '火',\r\n wednesday: '水',\r\n thursday: '木',\r\n friday: '金',\r\n saturday: '土',\r\n sunday: '日',\r\n january: '1月',\r\n february: '2月',\r\n march: '3月',\r\n april: '4月',\r\n may: '5月',\r\n june: '6月',\r\n july: '7月',\r\n august: '8月',\r\n september: '9月',\r\n october: '10月',\r\n november: '11月',\r\n december: '12月'\r\n },\r\n \r\n // Date Format\r\n dateFormat: {\r\n full: 'YYYY年MM月DD日',\r\n short: 'YYYY/MM/DD',\r\n monthDay: 'MM月DD日',\r\n yearMonth: 'YYYY年MM月'\r\n },\r\n \r\n // View Mode\r\n viewMode: {\r\n month: '月',\r\n week: '週',\r\n day: '日',\r\n hour: '時'\r\n },\r\n \r\n // Task\r\n task: {\r\n addRoot: 'ルートタスクを追加',\r\n addSub: 'サブタスクを追加',\r\n remove: 'タスクを削除',\r\n edit: 'タスクを編集',\r\n name: 'タスク名',\r\n priority: '優先度',\r\n startDate: '開始日',\r\n endDate: '終了日',\r\n duration: '期間',\r\n progress: '進捗',\r\n serialNumber: '番号'\r\n },\r\n \r\n // Link Legend\r\n link: {\r\n legend: 'リンク凡例',\r\n parentChild: '親子関係',\r\n finishToStart: '終了-開始',\r\n startToStart: '開始-開始',\r\n finishToFinish: '終了-終了',\r\n startToFinish: '開始-終了',\r\n fs: 'FS',\r\n ss: 'SS',\r\n ff: 'FF',\r\n sf: 'SF',\r\n pc: 'PC'\r\n },\r\n \r\n // Config Panel\r\n configPanel: {\r\n title: 'ガントチャート設定',\r\n themeSettings: 'テーマ設定',\r\n linkSettings: 'リンク設定',\r\n languageSettings: '言語設定',\r\n currentTheme: '現在',\r\n previewTheme: 'プレビュー',\r\n exportConfig: '設定をエクスポート',\r\n importConfig: '設定をインポート',\r\n \r\n // Link Configuration\r\n linkConfig: {\r\n info: 'タスク依存関係リンク(終了-開始、開始-開始など)を設定',\r\n pathType: 'パスタイプ',\r\n straight: '直線',\r\n bezier: 'ベジェ曲線',\r\n rightAngle: '直角',\r\n color: '色',\r\n width: '線の太さ',\r\n dashStyle: '破線スタイル',\r\n solid: '実線',\r\n shortDash: '短い破線',\r\n mediumDash: '中破線',\r\n longDash: '長い破線',\r\n dotDash: '点線破線',\r\n curvature: '曲率',\r\n offset: 'オフセット距離',\r\n smoothCorners: '滑らかな角',\r\n cornerRadius: '角の半径',\r\n showArrow: '矢印を表示',\r\n arrowSize: '矢印サイズ',\r\n arrowColor: '矢印の色',\r\n syncColor: '同期',\r\n dashAnimation: '破線アニメーション',\r\n animationSpeed: 'アニメーション速度',\r\n showLabels: 'ラベルを表示',\r\n labelColor: 'ラベルの色',\r\n fontSize: 'フォントサイズ',\r\n typeColors: '依存関係タイプの色',\r\n parentChildStyle: '親子リンクスタイル',\r\n parentChildInfo: '親タスクと子タスクの階層関係を表示'\r\n }\r\n },\r\n \r\n // Theme\r\n theme: {\r\n metro: 'メトロ',\r\n light: 'ライト',\r\n dark: 'ダーク',\r\n colorful: 'カラフル',\r\n ocean: 'オーシャン',\r\n apple: 'Apple',\r\n classic: 'クラシック',\r\n metroDesc: 'クラシックWindowsメトロスタイル',\r\n lightDesc: '明るく爽やかなライトスタイル',\r\n darkDesc: 'エレガントでプロフェッショナルなダークスタイル',\r\n colorfulDesc: '活気あるカラフルスタイル',\r\n oceanDesc: '落ち着いた快適なオーシャンスタイル',\r\n appleDesc: 'ミニマルでエレガントなmacOSスタイル',\r\n classicDesc: '伝統的で安定感のあるビジネススタイル'\r\n },\r\n \r\n // Date Picker\r\n datePicker: {\r\n selectDate: '日付を選択',\r\n clearDate: '日付をクリア'\r\n },\r\n \r\n // Tooltip\r\n tooltip: {\r\n addSubTask: 'サブタスクを追加',\r\n removeTask: '現在のタスクを削除',\r\n syncArrowColor: '線の色と同期',\r\n close: '閉じる'\r\n }\r\n};\r\n","/**\n * Korean language pack\n * 한국어 언어 팩\n */\nexport default {\n // Common\n common: {\n confirm: '확인',\n cancel: '취소',\n save: '저장',\n delete: '삭제',\n edit: '편집',\n add: '추가',\n close: '닫기',\n export: '내보내기',\n import: '가져오기',\n config: '설정',\n settings: '설정',\n theme: '테마',\n to: '에서',\n selectDate: '날짜 선택'\n },\n \n // Date & Time\n date: {\n year: '년',\n month: '월',\n day: '일',\n hour: '시',\n week: '주',\n today: '오늘',\n monday: '월',\n tuesday: '화',\n wednesday: '수',\n thursday: '목',\n friday: '금',\n saturday: '토',\n sunday: '일',\n january: '1월',\n february: '2월',\n march: '3월',\n april: '4월',\n may: '5월',\n june: '6월',\n july: '7월',\n august: '8월',\n september: '9월',\n october: '10월',\n november: '11월',\n december: '12월'\n },\n \n // Date Format\n dateFormat: {\n full: 'YYYY년 MM월 DD일',\n short: 'YYYY-MM-DD',\n monthDay: 'MM월 DD일',\n yearMonth: 'YYYY년 MM월'\n },\n \n // View Mode\n viewMode: {\n month: '월',\n week: '주',\n day: '일',\n hour: '시'\n },\n \n // Task\n task: {\n addRoot: '루트 작업 추가',\n addSub: '하위 작업 추가',\n remove: '작업 삭제',\n edit: '작업 편집',\n name: '작업 이름',\n priority: '우선순위',\n startDate: '시작일',\n endDate: '종료일',\n duration: '기간',\n progress: '진행률',\n serialNumber: '번호'\n },\n \n // Link Legend\n link: {\n legend: '링크 범례',\n parentChild: '부모-자식',\n finishToStart: '종료-시작',\n startToStart: '시작-시작',\n finishToFinish: '종료-종료',\n startToFinish: '시작-종료',\n fs: 'FS',\n ss: 'SS',\n ff: 'FF',\n sf: 'SF',\n pc: 'PC'\n },\n \n // Config Panel\n configPanel: {\n title: '간트 차트 설정',\n themeSettings: '테마 설정',\n linkSettings: '링크 설정',\n languageSettings: '언어 설정',\n currentTheme: '현재',\n previewTheme: '미리보기',\n exportConfig: '설정 내보내기',\n importConfig: '설정 가져오기',\n \n // Link Configuration\n linkConfig: {\n info: '작업 종속성 링크 설정 (종료-시작, 시작-시작 등)',\n pathType: '경로 유형',\n straight: '직선',\n bezier: '베지어 곡선',\n rightAngle: '직각',\n color: '색상',\n width: '선 두께',\n dashStyle: '파선 스타일',\n solid: '실선',\n shortDash: '짧은 파선',\n mediumDash: '중간 파선',\n longDash: '긴 파선',\n dotDash: '점선 파선',\n curvature: '곡률',\n offset: '오프셋 거리',\n smoothCorners: '부드러운 모서리',\n cornerRadius: '모서리 반경',\n showArrow: '화살표 표시',\n arrowSize: '화살표 크기',\n arrowColor: '화살표 색상',\n syncColor: '동기화',\n dashAnimation: '파선 애니메이션',\n animationSpeed: '애니메이션 속도',\n showLabels: '레이블 표시',\n labelColor: '레이블 색상',\n fontSize: '글꼴 크기',\n typeColors: '종속성 유형 색상',\n parentChildStyle: '부모-자식 링크 스타일',\n parentChildInfo: '부모 작업과 자식 작업의 계층 관계 표시'\n }\n },\n \n // Theme\n theme: {\n metro: '메트로',\n light: '라이트',\n dark: '다크',\n colorful: '컬러풀',\n ocean: '오션',\n apple: 'Apple',\n classic: '클래식',\n metroDesc: '클래식 Windows 메트로 스타일',\n lightDesc: '밝고 상쾌한 라이트 스타일',\n darkDesc: '우아하고 전문적인 다크 스타일',\n colorfulDesc: '활기찬 컬러풀 스타일',\n oceanDesc: '차분하고 편안한 오션 스타일',\n appleDesc: '미니멀하고 우아한 macOS 스타일',\n classicDesc: '전통적이고 안정적인 비즈니스 스타일'\n },\n \n // Date Picker\n datePicker: {\n selectDate: '날짜 선택',\n clearDate: '날짜 지우기'\n },\n \n // Tooltip\n tooltip: {\n addSubTask: '하위 작업 추가',\n removeTask: '현재 작업 삭제',\n syncArrowColor: '선 색상과 동기화',\n close: '닫기'\n }\n};\n","/**\n * French language pack\n * Pack de langue française\n */\nexport default {\n // Common\n common: {\n confirm: 'Confirmer',\n cancel: 'Annuler',\n save: 'Enregistrer',\n delete: 'Supprimer',\n edit: 'Modifier',\n add: 'Ajouter',\n close: 'Fermer',\n export: 'Exporter',\n import: 'Importer',\n config: 'Configuration',\n settings: 'Paramètres',\n theme: 'Thème',\n to: 'à',\n selectDate: 'Sélectionner la date'\n },\n \n // Date & Time\n date: {\n year: 'Année',\n month: 'Mois',\n day: 'Jour',\n hour: 'Heure',\n week: 'Semaine',\n today: \"Aujourd'hui\",\n monday: 'Lun',\n tuesday: 'Mar',\n wednesday: 'Mer',\n thursday: 'Jeu',\n friday: 'Ven',\n saturday: 'Sam',\n sunday: 'Dim',\n january: 'Janvier',\n february: 'Février',\n march: 'Mars',\n april: 'Avril',\n may: 'Mai',\n june: 'Juin',\n july: 'Juillet',\n august: 'Août',\n september: 'Septembre',\n october: 'Octobre',\n november: 'Novembre',\n december: 'Décembre'\n },\n \n // Date Format\n dateFormat: {\n full: 'DD MMMM YYYY',\n short: 'DD/MM/YYYY',\n monthDay: 'DD MMM',\n yearMonth: 'MMMM YYYY'\n },\n \n // View Mode\n viewMode: {\n month: 'Mois',\n week: 'Semaine',\n day: 'Jour',\n hour: 'Heure'\n },\n \n // Task\n task: {\n addRoot: 'Ajouter une tâche racine',\n addSub: 'Ajouter une sous-tâche',\n remove: 'Supprimer la tâche',\n edit: 'Modifier la tâche',\n name: 'Nom de la tâche',\n priority: 'Priorité',\n startDate: 'Date de début',\n endDate: 'Date de fin',\n duration: 'Durée',\n progress: 'Progrès',\n serialNumber: 'N°'\n },\n \n // Link Legend\n link: {\n legend: 'Légende des liens',\n parentChild: 'Parent-Enfant',\n finishToStart: 'Fin-Début',\n startToStart: 'Début-Début',\n finishToFinish: 'Fin-Fin',\n startToFinish: 'Début-Fin',\n fs: 'FD',\n ss: 'DD',\n ff: 'FF',\n sf: 'DF',\n pc: 'PE'\n },\n \n // Config Panel\n configPanel: {\n title: 'Configuration Gantt',\n themeSettings: 'Paramètres du thème',\n linkSettings: 'Paramètres des liens',\n languageSettings: 'Paramètres de langue',\n currentTheme: 'Actuel',\n previewTheme: 'Aperçu',\n exportConfig: 'Exporter la configuration',\n importConfig: 'Importer la configuration',\n \n // Link Configuration\n linkConfig: {\n info: 'Configurer les liens de dépendance des tâches (Fin-Début, Début-Début, etc.)',\n pathType: 'Type de chemin',\n straight: 'Droit',\n bezier: 'Bézier',\n rightAngle: 'Angle droit',\n color: 'Couleur',\n width: 'Épaisseur de ligne',\n dashStyle: 'Style de tiret',\n solid: 'Solide',\n shortDash: 'Tiret court',\n mediumDash: 'Tiret moyen',\n longDash: 'Tiret long',\n dotDash: 'Point-tiret',\n curvature: 'Courbure',\n offset: 'Distance de décalage',\n smoothCorners: 'Coins lisses',\n cornerRadius: 'Rayon du coin',\n showArrow: 'Afficher la flèche',\n arrowSize: 'Taille de la flèche',\n arrowColor: 'Couleur de la flèche',\n syncColor: 'Synchroniser',\n dashAnimation: 'Animation de tiret',\n animationSpeed: \"Vitesse d'animation\",\n showLabels: 'Afficher les étiquettes',\n labelColor: \"Couleur de l'étiquette\",\n fontSize: 'Taille de police',\n typeColors: 'Couleurs des types de dépendance',\n parentChildStyle: 'Style de lien parent-enfant',\n parentChildInfo: 'Afficher la relation hiérarchique entre les tâches parent et enfant'\n }\n },\n \n // Theme\n theme: {\n metro: 'Metro',\n light: 'Clair',\n dark: 'Sombre',\n colorful: 'Coloré',\n ocean: 'Océan',\n apple: 'Apple',\n classic: 'Classique',\n metroDesc: 'Style Metro Windows classique',\n lightDesc: 'Style clair frais et lumineux',\n darkDesc: 'Style sombre élégant et professionnel',\n colorfulDesc: 'Style coloré vibrant',\n oceanDesc: 'Style océan calme et confortable',\n appleDesc: 'Style macOS minimaliste et élégant',\n classicDesc: 'Style business traditionnel et stable'\n },\n \n // Date Picker\n datePicker: {\n selectDate: 'Sélectionner la date',\n clearDate: 'Effacer la date'\n },\n \n // Tooltip\n tooltip: {\n addSubTask: 'Ajouter une sous-tâche',\n removeTask: 'Supprimer la tâche actuelle',\n syncArrowColor: 'Synchroniser avec la couleur de la ligne',\n close: 'Fermer'\n }\n};\n","/**\r\n * German language pack\r\n * Deutsches Sprachpaket\r\n */\r\nexport default {\r\n // Common\r\n common: {\r\n confirm: 'Bestätigen',\r\n cancel: 'Abbrechen',\r\n save: 'Speichern',\r\n delete: 'Löschen',\r\n edit: 'Bearbeiten',\r\n add: 'Hinzufügen',\r\n close: 'Schließen',\r\n export: 'Exportieren',\r\n import: 'Importieren',\r\n config: 'Konfiguration',\r\n settings: 'Einstellungen',\r\n theme: 'Thema',\r\n to: 'bis',\r\n selectDate: 'Datum auswählen'\r\n },\r\n \r\n // Date & Time\r\n date: {\r\n year: 'Jahr',\r\n month: 'Monat',\r\n day: 'Tag',\r\n hour: 'Stunde',\r\n week: 'Woche',\r\n today: 'Heute',\r\n monday: 'Mo',\r\n tuesday: 'Di',\r\n wednesday: 'Mi',\r\n thursday: 'Do',\r\n friday: 'Fr',\r\n saturday: 'Sa',\r\n sunday: 'So',\r\n january: 'Januar',\r\n february: 'Februar',\r\n march: 'März',\r\n april: 'April',\r\n may: 'Mai',\r\n june: 'Juni',\r\n july: 'Juli',\r\n august: 'August',\r\n september: 'September',\r\n october: 'Oktober',\r\n november: 'November',\r\n december: 'Dezember'\r\n },\r\n \r\n // Date Format\r\n dateFormat: {\r\n full: 'DD. MMMM YYYY',\r\n short: 'DD.MM.YYYY',\r\n monthDay: 'DD. MMM',\r\n yearMonth: 'MMMM YYYY'\r\n },\r\n \r\n // View Mode\r\n viewMode: {\r\n month: 'Monat',\r\n week: 'Woche',\r\n day: 'Tag',\r\n hour: 'Stunde'\r\n },\r\n \r\n // Task\r\n task: {\r\n addRoot: 'Hauptaufgabe hinzufügen',\r\n addSub: 'Unteraufgabe hinzufügen',\r\n remove: 'Aufgabe löschen',\r\n edit: 'Aufgabe bearbeiten',\r\n name: 'Aufgabenname',\r\n priority: 'Priorität',\r\n startDate: 'Startdatum',\r\n endDate: 'Enddatum',\r\n duration: 'Dauer',\r\n progress: 'Fortschritt',\r\n serialNumber: 'Nr.'\r\n },\r\n \r\n // Link Legend\r\n link: {\r\n legend: 'Verknüpfungslegende',\r\n parentChild: 'Übergeordnet-Untergeordnet',\r\n finishToStart: 'Ende-Anfang',\r\n startToStart: 'Anfang-Anfang',\r\n finishToFinish: 'Ende-Ende',\r\n startToFinish: 'Anfang-Ende',\r\n fs: 'EA',\r\n ss: 'AA',\r\n ff: 'EE',\r\n sf: 'AE',\r\n pc: 'ÜU'\r\n },\r\n \r\n // Config Panel\r\n configPanel: {\r\n title: 'Gantt-Konfiguration',\r\n themeSettings: 'Themeneinstellungen',\r\n linkSettings: 'Verknüpfungseinstellungen',\r\n languageSettings: 'Spracheinstellungen',\r\n currentTheme: 'Aktuell',\r\n previewTheme: 'Vorschau',\r\n exportConfig: 'Konfiguration exportieren',\r\n importConfig: 'Konfiguration importieren',\r\n \r\n // Link Configuration\r\n linkConfig: {\r\n info: 'Aufgabenabhängigkeitsverknüpfungen konfigurieren (Ende-Anfang, Anfang-Anfang usw.)',\r\n pathType: 'Pfadtyp',\r\n straight: 'Gerade',\r\n bezier: 'Bézier',\r\n rightAngle: 'Rechter Winkel',\r\n color: 'Farbe',\r\n width: 'Linienstärke',\r\n dashStyle: 'Strichstil',\r\n solid: 'Durchgezogen',\r\n shortDash: 'Kurzer Strich',\r\n mediumDash: 'Mittlerer Strich',\r\n longDash: 'Langer Strich',\r\n dotDash: 'Punkt-Strich',\r\n curvature: 'Krümmung',\r\n offset: 'Versatzabstand',\r\n smoothCorners: 'Glatte Ecken',\r\n cornerRadius: 'Eckenradius',\r\n showArrow: 'Pfeil anzeigen',\r\n arrowSize: 'Pfeilgröße',\r\n arrowColor: 'Pfeilfarbe',\r\n syncColor: 'Synchronisieren',\r\n dashAnimation: 'Strich-Animation',\r\n animationSpeed: 'Animationsgeschwindigkeit',\r\n showLabels: 'Beschriftungen anzeigen',\r\n labelColor: 'Beschriftungsfarbe',\r\n fontSize: 'Schriftgröße',\r\n typeColors: 'Abhängigkeitstypfarben',\r\n parentChildStyle: 'Übergeordnet-Untergeordnet-Verknüpfungsstil',\r\n parentChildInfo: 'Hierarchische Beziehung zwischen übergeordneten und untergeordneten Aufgaben anzeigen'\r\n }\r\n },\r\n \r\n // Theme\r\n theme: {\r\n metro: 'Metro',\r\n light: 'Hell',\r\n dark: 'Dunkel',\r\n colorful: 'Farbenfroh',\r\n ocean: 'Ozean',\r\n apple: 'Apple',\r\n classic: 'Klassisch',\r\n metroDesc: 'Klassischer Windows Metro-Stil',\r\n lightDesc: 'Frischer und heller Stil',\r\n darkDesc: 'Eleganter und professioneller dunkler Stil',\r\n colorfulDesc: 'Lebendiger farbenfroher Stil',\r\n oceanDesc: 'Ruhiger und komfortabler Ozean-Stil',\r\n appleDesc: 'Minimalistischer und eleganter macOS-Stil',\r\n classicDesc: 'Traditioneller und stabiler Geschäftsstil'\r\n },\r\n \r\n // Date Picker\r\n datePicker: {\r\n selectDate: 'Datum auswählen',\r\n clearDate: 'Datum löschen'\r\n },\r\n \r\n // Tooltip\r\n tooltip: {\r\n addSubTask: 'Unteraufgabe hinzufügen',\r\n removeTask: 'Aktuelle Aufgabe löschen',\r\n syncArrowColor: 'Mit Linienfarbe synchronisieren',\r\n close: 'Schließen'\r\n }\r\n};\r\n","/**\n * Spanish language pack\n * Paquete de idioma español\n */\nexport default {\n // Common\n common: {\n confirm: 'Confirmar',\n cancel: 'Cancelar',\n save: 'Guardar',\n delete: 'Eliminar',\n edit: 'Editar',\n add: 'Agregar',\n close: 'Cerrar',\n export: 'Exportar',\n import: 'Importar',\n config: 'Configuración',\n settings: 'Ajustes',\n theme: 'Tema',\n to: 'a',\n selectDate: 'Seleccionar fecha'\n },\n \n // Date & Time\n date: {\n year: 'Año',\n month: 'Mes',\n day: 'Día',\n hour: 'Hora',\n week: 'Semana',\n today: 'Hoy',\n monday: 'Lun',\n tuesday: 'Mar',\n wednesday: 'Mié',\n thursday: 'Jue',\n friday: 'Vie',\n saturday: 'Sáb',\n sunday: 'Dom',\n january: 'Enero',\n february: 'Febrero',\n march: 'Marzo',\n april: 'Abril',\n may: 'Mayo',\n june: 'Junio',\n july: 'Julio',\n august: 'Agosto',\n september: 'Septiembre',\n october: 'Octubre',\n november: 'Noviembre',\n december: 'Diciembre'\n },\n \n // Date Format\n dateFormat: {\n full: 'DD [de] MMMM [de] YYYY',\n short: 'DD/MM/YYYY',\n monthDay: 'DD MMM',\n yearMonth: 'MMMM YYYY'\n },\n \n // View Mode\n viewMode: {\n month: 'Mes',\n week: 'Semana',\n day: 'Día',\n hour: 'Hora'\n },\n \n // Task\n task: {\n addRoot: 'Agregar tarea raíz',\n addSub: 'Agregar subtarea',\n remove: 'Eliminar tarea',\n edit: 'Editar tarea',\n name: 'Nombre de tarea',\n priority: 'Prioridad',\n startDate: 'Fecha de inicio',\n endDate: 'Fecha de fin',\n duration: 'Duración',\n progress: 'Progreso',\n serialNumber: 'Nº'\n },\n \n // Link Legend\n link: {\n legend: 'Leyenda de enlaces',\n parentChild: 'Padre-Hijo',\n finishToStart: 'Fin-Inicio',\n startToStart: 'Inicio-Inicio',\n finishToFinish: 'Fin-Fin',\n startToFinish: 'Inicio-Fin',\n fs: 'FI',\n ss: 'II',\n ff: 'FF',\n sf: 'IF',\n pc: 'PH'\n },\n \n // Config Panel\n configPanel: {\n title: 'Configuración Gantt',\n themeSettings: 'Configuración de tema',\n linkSettings: 'Configuración de enlaces',\n languageSettings: 'Configuración de idioma',\n currentTheme: 'Actual',\n previewTheme: 'Vista previa',\n exportConfig: 'Exportar configuración',\n importConfig: 'Importar configuración',\n \n // Link Configuration\n linkConfig: {\n info: 'Configurar enlaces de dependencia de tareas (Fin-Inicio, Inicio-Inicio, etc.)',\n pathType: 'Tipo de ruta',\n straight: 'Recta',\n bezier: 'Bézier',\n rightAngle: 'Ángulo recto',\n color: 'Color',\n width: 'Grosor de línea',\n dashStyle: 'Estilo de guión',\n solid: 'Sólido',\n shortDash: 'Guión corto',\n mediumDash: 'Guión medio',\n longDash: 'Guión largo',\n dotDash: 'Punto-guión',\n curvature: 'Curvatura',\n offset: 'Distancia de desplazamiento',\n smoothCorners: 'Esquinas suaves',\n cornerRadius: 'Radio de esquina',\n showArrow: 'Mostrar flecha',\n arrowSize: 'Tamaño de flecha',\n arrowColor: 'Color de flecha',\n syncColor: 'Sincronizar',\n dashAnimation: 'Animación de guión',\n animationSpeed: 'Velocidad de animación',\n showLabels: 'Mostrar etiquetas',\n labelColor: 'Color de etiqueta',\n fontSize: 'Tamaño de fuente',\n typeColors: 'Colores de tipo de dependencia',\n parentChildStyle: 'Estilo de enlace padre-hijo',\n parentChildInfo: 'Mostrar relación jerárquica entre tareas padre e hijo'\n }\n },\n \n // Theme\n theme: {\n metro: 'Metro',\n light: 'Claro',\n dark: 'Oscuro',\n colorful: 'Colorido',\n ocean: 'Océano',\n apple: 'Apple',\n classic: 'Clásico',\n metroDesc: 'Estilo Metro Windows clásico',\n lightDesc: 'Estilo claro fresco y brillante',\n darkDesc: 'Estilo oscuro elegante y profesional',\n colorfulDesc: 'Estilo colorido vibrante',\n oceanDesc: 'Estilo océano tranquilo y cómodo',\n appleDesc: 'Estilo macOS minimalista y elegante',\n classicDesc: 'Estilo empresarial tradicional y estable'\n },\n \n // Date Picker\n datePicker: {\n selectDate: 'Seleccionar fecha',\n clearDate: 'Borrar fecha'\n },\n \n // Tooltip\n tooltip: {\n addSubTask: 'Agregar subtarea',\n removeTask: 'Eliminar tarea actual',\n syncArrowColor: 'Sincronizar con color de línea',\n close: 'Cerrar'\n }\n};\n","/**\r\n * Russian language pack\r\n * Русский языковой пакет\r\n */\r\nexport default {\r\n // Common\r\n common: {\r\n confirm: 'Подтвердить',\r\n cancel: 'Отмена',\r\n save: 'Сохранить',\r\n delete: 'Удалить',\r\n edit: 'Редактировать',\r\n add: 'Добавить',\r\n close: 'Закрыть',\r\n export: 'Экспорт',\r\n import: 'Импорт',\r\n config: 'Конфигурация',\r\n settings: 'Настройки',\r\n theme: 'Тема',\r\n to: 'до',\r\n selectDate: 'Выбрать дату'\r\n },\r\n \r\n // Date & Time\r\n date: {\r\n year: 'Год',\r\n month: 'Месяц',\r\n day: 'День',\r\n hour: 'Час',\r\n week: 'Неделя',\r\n today: 'Сегодня',\r\n monday: 'Пн',\r\n tuesday: 'Вт',\r\n wednesday: 'Ср',\r\n thursday: 'Чт',\r\n friday: 'Пт',\r\n saturday: 'Сб',\r\n sunday: 'Вс',\r\n january: 'Январь',\r\n february: 'Февраль',\r\n march: 'Март',\r\n april: 'Апрель',\r\n may: 'Май',\r\n june: 'Июнь',\r\n july: 'Июль',\r\n august: 'Август',\r\n september: 'Сентябрь',\r\n october: 'Октябрь',\r\n november: 'Ноябрь',\r\n december: 'Декабрь'\r\n },\r\n \r\n // Date Format\r\n dateFormat: {\r\n full: 'DD MMMM YYYY г.',\r\n short: 'DD.MM.YYYY',\r\n monthDay: 'DD MMM',\r\n yearMonth: 'MMMM YYYY'\r\n },\r\n \r\n // View Mode\r\n viewMode: {\r\n month: 'Месяц',\r\n week: 'Неделя',\r\n day: 'День',\r\n hour: 'Час'\r\n },\r\n \r\n // Task\r\n task: {\r\n addRoot: 'Добавить корневую задачу',\r\n addSub: 'Добавить подзадачу',\r\n remove: 'Удалить задачу',\r\n edit: 'Редактировать задачу',\r\n name: 'Название задачи',\r\n priority: 'Приоритет',\r\n startDate: 'Дата начала',\r\n endDate: 'Дата окончания',\r\n duration: 'Длительность',\r\n progress: 'Прогресс',\r\n serialNumber: '№'\r\n },\r\n \r\n // Link Legend\r\n link: {\r\n legend: 'Легенда связей',\r\n parentChild: 'Родитель-Потомок',\r\n finishToStart: 'Окончание-Начало',\r\n startToStart: 'Начало-Начало',\r\n finishToFinish: 'Окончание-Окончание',\r\n startToFinish: 'Начало-Окончание',\r\n fs: 'ОН',\r\n ss: 'НН',\r\n ff: 'ОО',\r\n sf: 'НО',\r\n pc: 'РП'\r\n },\r\n \r\n // Config Panel\r\n configPanel: {\r\n title: 'Конфигурация Gantt',\r\n themeSettings: 'Настройки темы',\r\n linkSettings: 'Настройки связей',\r\n languageSettings: 'Настройки языка',\r\n currentTheme: 'Текущая',\r\n previewTheme: 'Предпросмотр',\r\n exportConfig: 'Экспорт конфигурации',\r\n importConfig: 'Импорт конфигурации',\r\n \r\n // Link Configuration\r\n linkConfig: {\r\n info: 'Настройка связей зависимостей задач (Окончание-Начало, Начало-Начало и т.д.)',\r\n pathType: 'Тип пути',\r\n straight: 'Прямая',\r\n bezier: 'Безье',\r\n rightAngle: 'Прямой угол',\r\n color: 'Цвет',\r\n width: 'Толщина линии',\r\n dashStyle: 'Стиль пунктира',\r\n solid: 'Сплошная',\r\n shortDash: 'Короткий пунктир',\r\n mediumDash: 'Средний пунктир',\r\n longDash: 'Длинный пунктир',\r\n dotDash: 'Точка-тире',\r\n curvature: 'Кривизна',\r\n offset: 'Расстояние смещения',\r\n smoothCorners: 'Сглаженные углы',\r\n cornerRadius: 'Радиус угла',\r\n showArrow: 'Показать стрелку',\r\n arrowSize: 'Размер стрелки',\r\n arrowColor: 'Цвет стрелки',\r\n syncColor: 'Синхронизировать',\r\n dashAnimation: 'Анимация пунктира',\r\n animationSpeed: 'Скорость анимации',\r\n showLabels: 'Показать метки',\r\n labelColor: 'Цвет метки',\r\n fontSize: 'Размер шрифта',\r\n typeColors: 'Цвета типов зависимостей',\r\n parentChildStyle: 'Стиль связи родитель-потомок',\r\n parentChildInfo: 'Показать иерархическую связь между родительскими и дочерними задачами'\r\n }\r\n },\r\n \r\n // Theme\r\n theme: {\r\n metro: 'Метро',\r\n light: 'Светлая',\r\n dark: 'Темная',\r\n colorful: 'Красочная',\r\n ocean: 'Океан',\r\n apple: 'Apple',\r\n classic: 'Классический',\r\n metroDesc: 'Классический стиль Windows Metro',\r\n lightDesc: 'Свежий и яркий светлый стиль',\r\n darkDesc: 'Элегантный и профессиональный темный стиль',\r\n colorfulDesc: 'Яркий красочный стиль',\r\n oceanDesc: 'Спокойный и комфортный океанский стиль',\r\n appleDesc: 'Минималистичный и элегантный стиль macOS',\r\n classicDesc: 'Традиционный и стабильный деловой стиль'\r\n },\r\n \r\n // Date Picker\r\n datePicker: {\r\n selectDate: 'Выбрать дату',\r\n clearDate: 'Очистить дату'\r\n },\r\n \r\n // Tooltip\r\n tooltip: {\r\n addSubTask: 'Добавить подзадачу',\r\n removeTask: 'Удалить текущую задачу',\r\n syncArrowColor: 'Синхронизировать с цветом линии',\r\n close: 'Закрыть'\r\n }\r\n};\r\n","<template>\r\n <div>\r\n <!-- 添加文本框 -->\r\n <div class=\"date-picker-input-wrapper\">\r\n <input type=\"text\" v-model=\"selectedDateText\" @click=\"showCalendar = true\" readonly :placeholder=\"t('datePicker.selectDate')\"\r\n class=\"date-picker-input\" ref=\"inputRef\" />\r\n <span class=\"clear-date-button\" @click=\"clearDate\" v-if=\"selectedDateText\">\r\n <svg viewBox=\"0 0 24 24\">\r\n <path\r\n d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\">\r\n </path>\r\n </svg>\r\n </span>\r\n </div>\r\n <!-- 日期选择器 -->\r\n <div class=\"e-calendar\" v-show=\"showCalendar\" ref=\"calendarRef\">\r\n <div class=\"e-date-select\">\r\n <div class=\"e-date-year\">\r\n <transition name=\"fadeY\">\r\n <div :key=\"selectDate.year\" class=\"e-date-year-select\" @click=\"openYearList\" :class=\"{ active: showYear }\">\r\n {{ selectDate.year }}\r\n </div>\r\n </transition>\r\n </div>\r\n <div class=\"e-date-monthday\">\r\n <transition name=\"fadeY\">\r\n <div :key=\"selectDate.day\" class=\"e-date-monthday-select\" :class=\"{ active: !showYear }\"\r\n @click=\"openCalendarList\">\r\n <span>{{ keepDoubleDigit(selectDate.month) }}-{{ keepDoubleDigit(selectDate.day) }}</span>&nbsp;\r\n <span style=\"cursor: pointer;\" @click=\"openMonthList\">{{ showDate.monthStr }}</span>\r\n </div>\r\n </transition>\r\n </div>\r\n </div>\r\n\r\n <div class=\"e-calendar-container\" v-show=\"!showYear && !showMonth\">\r\n <div class=\"e-calendar-toolbar\">\r\n <div class=\"e-calendar-svg\" @click=\"prevMonth\">\r\n <svg viewBox=\"0 0 24 24\" class=\"e-calendar-svg-icon\">\r\n <path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\"></path>\r\n </svg>\r\n <transition name=\"e_calendar_svg_btn\">\r\n <div class=\"e-calendar-svg-cover\" v-if=\"prevMonthClick\"></div>\r\n </transition>\r\n </div>\r\n <div class=\"e-calendar-toolbar-title\">\r\n <transition :name=\"fadeXType\">\r\n <div :key=\"showDate.monthStr\" class=\"e-calendar-toolbar-title-content\">\r\n <strong>{{ showDate.year }}</strong>&nbsp;\r\n <span style=\"cursor: pointer;\" @click=\"openMonthList\">{{ showDate.monthStr }}</span>\r\n </div>\r\n </transition>\r\n </div>\r\n <div class=\"e-calendar-svg\" @click=\"nextMonth\">\r\n <svg viewBox=\"0 0 24 24\" class=\"e-calendar-svg-icon\">\r\n <path d=\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\"></path>\r\n </svg>\r\n <transition name=\"e_calendar_svg_btn\">\r\n <div class=\"e-calendar-svg-cover\" v-if=\"nextMonthClick\"></div>\r\n </transition>\r\n </div>\r\n </div>\r\n <div class=\"e-calendar-week\">\r\n <span v-for=\"(weekName, index) in getWeekNames\" :key=\"index\" class=\"e-calendar-week-day\">{{ weekName }}</span>\r\n </div>\r\n <div class=\"e-calendar-monthday\">\r\n <transition :name=\"fadeXType\">\r\n <div :key=\"showDate.monthStr\" class=\"e-calendar-monthday-wrapper\">\r\n <div v-for=\"(row, index) in rows\" :key=\"index\" class=\"e-calendar-monthday-row\">\r\n <span v-for=\"(day, dayIndex) in row\" :key=\"dayIndex\" class=\"e-calendar-monthday-row-day\"\r\n @click=\"selectDay(day)\" @mouseenter=\"handleDayMouseEnter(dayIndex, index)\"\r\n @mouseleave=\"handleDayMouseLeave()\" :class=\"{\r\n active: day.selected,\r\n disabled: day.disabled,\r\n pointer: day.value !== '',\r\n hover: isDayHovered(dayIndex, index)\r\n }\">\r\n <span v-text=\"day.value\" class=\"e-calendar-monthday-row-day-value\"></span>\r\n <transition name=\"e_calendar_day\">\r\n <span class=\"e-calendar-monthday-row-day-cover\" v-if=\"day.selected\"></span>\r\n </transition>\r\n </span>\r\n </div>\r\n </div>\r\n </transition>\r\n </div>\r\n </div>\r\n <ul class=\"e-calendar-year\" v-show=\"showYear\" ref=\"yearList\">\r\n <li v-for=\"(item, index) in yearList\" :key=\"index\" :class=\"{\r\n active: item === selectDate.year,\r\n hover: isYearHovered(index)\r\n }\" @click=\"selectYear(item)\" @mouseenter=\"handleYearMouseEnter(index)\" @mouseleave=\"handleYearMouseLeave()\">\r\n {{ item }}\r\n </li>\r\n </ul>\r\n <ul class=\"e-calendar-year\" v-show=\"showMonth\" ref=\"monthList\">\r\n <li v-for=\"(item, index) in monthList\" :key=\"index\" :class=\"{\r\n active: item === selectDate.month,\r\n hover: isMonthHovered(index)\r\n }\" @click=\"selectMonth(item)\" @mouseenter=\"handleMonthMouseEnter(index)\" @mouseleave=\"handleMonthMouseLeave()\">\r\n {{ getMonthName(item) }}\r\n </li>\r\n </ul>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, computed, onBeforeMount, onMounted, onUnmounted, watchEffect, watch } from 'vue';\r\nimport { useI18n } from './i18n';\r\nimport dayjs from 'dayjs';\r\nimport 'dayjs/locale/zh-cn';\r\nimport 'dayjs/locale/en';\r\nimport 'dayjs/locale/ja';\r\nimport 'dayjs/locale/ko';\r\nimport 'dayjs/locale/fr';\r\nimport 'dayjs/locale/de';\r\nimport 'dayjs/locale/es';\r\nimport 'dayjs/locale/ru';\r\n\r\nexport default defineComponent({\r\n props: {\r\n // 打开date picker的初始值,必传,格式是(2017-08-11)\r\n date: {\r\n type: String,\r\n required: true,\r\n },\r\n // 日期最小值\r\n minDate: String,\r\n // 日期最大值\r\n maxDate: String,\r\n },\r\n setup(props, { emit }) {\r\n const { t, locale } = useI18n();\r\n \r\n // 获取 dayjs locale\r\n const getDayjsLocale = () => {\r\n const localeMap: Record<string, string> = {\r\n 'zh-CN': 'zh-cn',\r\n 'en-US': 'en',\r\n 'ja-JP': 'ja',\r\n 'ko-KR': 'ko',\r\n 'fr-FR': 'fr',\r\n 'de-DE': 'de',\r\n 'es-ES': 'es',\r\n 'ru-RU': 'ru'\r\n };\r\n return localeMap[locale.value] || 'en';\r\n };\r\n \r\n // 获取星期名称\r\n const getWeekNames = computed(() => {\r\n const dayjsLocale = getDayjsLocale();\r\n const weekNames = [];\r\n for (let i = 1; i <= 7; i++) {\r\n // 使用 dayjs 获取本地化的星期名称(简写)\r\n const day = dayjs().day(i).locale(dayjsLocale);\r\n weekNames.push(day.format('dd'));\r\n }\r\n return weekNames;\r\n });\r\n \r\n // 获取月份名称\r\n const getMonthName = (month: number) => {\r\n const dayjsLocale = getDayjsLocale();\r\n return dayjs().month(month - 1).locale(dayjsLocale).format('MMMM');\r\n };\r\n \r\n // 定义响应式数据\r\n let selectDate = ref({\r\n year: 0,\r\n month: 0,\r\n day: 0,\r\n week: 0,\r\n date: '',\r\n weekStr: '',\r\n monthStr: '',\r\n });\r\n let showDate = ref({\r\n year: 0,\r\n month: 0,\r\n day: 0,\r\n week: 0,\r\n date: '',\r\n monthStr: '',\r\n weekStr: '',\r\n });\r\n let copyMinDate = ref({\r\n year: 0,\r\n month: 0,\r\n day: 0,\r\n });\r\n let copyMaxDate = ref({\r\n year: 0,\r\n month: 0,\r\n day: 0,\r\n });\r\n\r\n const fadeXType = ref('fadeX_Prev');\r\n const nextMonthClick = ref(false);\r\n const prevMonthClick = ref(false);\r\n const showYear = ref(false);\r\n const showMonth = ref(false);\r\n const yearListRef = ref<HTMLUListElement | null>(null);\r\n const monthListRef = ref<HTMLUListElement | null>(null);\r\n const showCalendar = ref(false); // 控制日期选择器的显示和隐藏\r\n const selectedDateText = ref(''); // 文本框显示的日期\r\n const calendarRef = ref<HTMLDivElement | null>(null); // 日期组件的引用\r\n const inputRef = ref<HTMLInputElement | null>(null); // 新增文本框引用\r\n\r\n const hoveredYearIndex = ref(-1);\r\n const hoveredMonthIndex = ref(-1);\r\n const hoveredDayIndex = ref({ row: -1, col: -1 });\r\n\r\n const yearList = computed(() => {\r\n const result: number[] = [];\r\n for (let i = copyMinDate.value.year; i <= copyMaxDate.value.year; i += 1) {\r\n result.push(i);\r\n }\r\n return result;\r\n });\r\n\r\n const monthList = computed(() => {\r\n const result: number[] = [];\r\n for (let i = 1; i <= 12; i += 1) {\r\n result.push(i);\r\n }\r\n return result;\r\n });\r\n\r\n const rows = computed(() => {\r\n const { year, month } = showDate.value;\r\n const months = new Date(year, month, 0).getDate();\r\n const result: {\r\n value: number | string;\r\n selected?: boolean;\r\n disabled?: boolean;\r\n }[][] = [];\r\n let row: {\r\n value: number | string;\r\n selected?: boolean;\r\n disabled?: boolean;\r\n }[] = [];\r\n let weekValue: number;\r\n // 按照星期分组\r\n for (let i = 1; i <= months; i += 1) {\r\n // 根据日期获取星期,并让开头是1,而非0\r\n weekValue = new Date(year, month - 1, i).getDay() + 1;\r\n // 判断月第一天在星期几,并填充前面的空白区域\r\n if (i === 1 && weekValue !== 1) {\r\n addRowEmptyValue(row, weekValue);\r\n addRowDayValue(row, i);\r\n } else {\r\n addRowDayValue(row, i);\r\n // 判断月最后一天在星期几,并填充后面的空白区域\r\n if (i === months && weekValue !== 7) {\r\n addRowEmptyValue(row, (7 - weekValue) + 1);\r\n }\r\n }\r\n // 按照一周分组\r\n if (weekValue % 7 === 0 || i === months) {\r\n result.push(row);\r\n row = [];\r\n }\r\n }\r\n showDate.value.monthStr = getMonthName(showDate.value.month);\r\n return result;\r\n });\r\n\r\n // 定义函数,接收一个 number 类型的参数,返回一个 string 类型的值\r\n const keepDoubleDigit = (number: number): string => {\r\n // 判断传入的数字是否大于 9\r\n return number > 9 ? String(number) : `0${number}`;\r\n };\r\n\r\n // 拆分日期\r\n const splitDate = (date: string, addStr = false) => {\r\n let result: {\r\n year: number;\r\n month: number;\r\n day: number;\r\n week?: number;\r\n monthStr?: string;\r\n weekStr?: string;\r\n } = {\r\n year: 0,\r\n month: 0,\r\n day: 0,\r\n };\r\n const splitValue = date.split('-');\r\n try {\r\n if (!splitValue || splitValue.length < 3) {\r\n throw new Error('时间格式不正确');\r\n }\r\n result = {\r\n year: Number(splitValue[0]),\r\n month: Number(splitValue[1]),\r\n day: Number(splitValue[2]),\r\n };\r\n if (addStr) {\r\n result.week = new Date(result.year, result.month - 1, result.day).getDay() + 1;\r\n result.monthStr = getMonthName(result.month);\r\n const dayjsLocale = getDayjsLocale();\r\n result.weekStr = dayjs().day(result.week).locale(dayjsLocale).format('dddd');\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n return result;\r\n };\r\n\r\n // 初始化日期选择器\r\n const initDatePicker = () => {\r\n const splitResult = splitDate(props.date, true);\r\n showDate.value = {\r\n ...splitResult,\r\n date: `${splitResult.year}-${keepDoubleDigit(splitResult.month)}-${keepDoubleDigit(\r\n splitResult.day\r\n )}`,\r\n week: splitResult.week || 0,\r\n monthStr: splitResult.monthStr || '',\r\n weekStr: splitResult.weekStr || '',\r\n };\r\n copyMinDate.value = { ...splitDate(props.minDate || '1970-01-01') };\r\n copyMaxDate.value = { ...splitDate(props.maxDate || '2099-12-31') };\r\n selectDate.value = { ...showDate.value };\r\n \r\n // 使用国际化日期格式\r\n const dayjsLocale = getDayjsLocale();\r\n const dateObj = dayjs(showDate.value.date).locale(dayjsLocale);\r\n const dateFormat = t('dateFormat.full');\r\n selectedDateText.value = dateObj.format(dateFormat);\r\n };\r\n\r\n watchEffect(() => {\r\n if (props.date) {\r\n initDatePicker(); // 当 props.date 变化时重新初始化日期选择器\r\n }\r\n });\r\n\r\n onBeforeMount(() => {\r\n initDatePicker();\r\n });\r\n\r\n // 添加空值\r\n const addRowEmptyValue = (\r\n row: {\r\n value: number | string;\r\n selected?: boolean;\r\n disabled?: boolean;\r\n }[],\r\n count: number\r\n ) => {\r\n for (let w = 1; w < count; w += 1) {\r\n row.push({\r\n value: '',\r\n });\r\n }\r\n };\r\n\r\n // 添加日期值\r\n const addRowDayValue = (\r\n row: {\r\n value: number | string;\r\n selected?: boolean;\r\n disabled?: boolean;\r\n }[],\r\n i: number\r\n ) => {\r\n const value = { value: i };\r\n const { day, month, year } = selectDate.value;\r\n const showDateValue = showDate.value;\r\n // 判断已经选择的\r\n if (year === showDateValue.year && month === showDateValue.month && day === i) {\r\n // 为对象添加 selected 属性\r\n const newValue = { ...value, selected: true };\r\n row.push(newValue);\r\n return; // 添加 return 避免重复添加\r\n }\r\n // 当日期在最小值之外,禁止点击\r\n if (isMinLimitMonth() && i < copyMinDate.value.day) {\r\n // 修复:添加 disabled 属性\r\n const newValue = { ...value, disabled: true };\r\n row.push(newValue);\r\n return; // 添加 return 避免重复添加\r\n }\r\n // 当日期在最大值之外,禁止点击\r\n if (isMaxLimitMonth() && i > copyMaxDate.value.day) {\r\n // 修复:添加 disabled 属性\r\n const newValue = { ...value, disabled: true };\r\n row.push(newValue);\r\n return; // 添加 return 避免重复添加\r\n }\r\n row.push(value);\r\n };\r\n\r\n // 切换到上一个月\r\n const prevMonth = () => {\r\n if (prevMonthClick.value) {\r\n return;\r\n }\r\n prevMonthClick.value = true;\r\n setTimeout(() => {\r\n prevMonthClick.value = false;\r\n }, 500);\r\n fadeXType.value = 'fadeX_Prev';\r\n // 如何当前月份已经小于等于minMonth 就不让其在执行\r\n if (isMinLimitMonth()) {\r\n return;\r\n }\r\n const { year, month } = showDate.value;\r\n // 判断当前月份,如果已经等于1(1就是一月,而不是二月)\r\n if (month <= 1) {\r\n showDate.value.year = year - 1;\r\n showDate.value.month = 12;\r\n } else {\r\n showDate.value.month -= 1;\r\n }\r\n };\r\n\r\n // 切换到下一个月\r\n const nextMonth = () => {\r\n if (nextMonthClick.value) {\r\n return;\r\n }\r\n nextMonthClick.value = true;\r\n setTimeout(() => {\r\n nextMonthClick.value = false;\r\n }, 500);\r\n fadeXType.value = 'fadeX_Next';\r\n // 如何当前月份已经大于等于maxMonth 就不让其在执行\r\n if (isMaxLimitMonth()) {\r\n return;\r\n }\r\n const { year, month } = showDate.value;\r\n // 判断当前月份,如果已经等于12(12就是十二月)\r\n if (month >= 12) {\r\n showDate.value.year = year + 1;\r\n showDate.value.month = 1;\r\n } else {\r\n showDate.value.month += 1;\r\n }\r\n };\r\n\r\n // 重置选择日期\r\n let resetSelectDate = (dayValue: number) => {\r\n // 修改为逐个修改属性\r\n selectDate.value.year = showDate.value.year;\r\n selectDate.value.month = showDate.value.month;\r\n selectDate.value.week = showDate.value.week;\r\n selectDate.value.date = showDate.value.date;\r\n selectDate.value.weekStr = showDate.value.weekStr;\r\n selectDate.value.monthStr = showDate.value.monthStr;\r\n selectDate.value.day = dayValue;\r\n selectDate.value.week = new Date(showDate.value.year, showDate.value.month - 1, dayValue).getDay() + 1;\r\n const dayjsLocale = getDayjsLocale();\r\n selectDate.value.weekStr = dayjs().day(selectDate.value.week).locale(dayjsLocale).format('dddd');\r\n \r\n // 使用国际化日期格式\r\n const dateObj = dayjs(`${selectDate.value.year}-${keepDoubleDigit(selectDate.value.month)}-${keepDoubleDigit(selectDate.value.day)}`).locale(dayjsLocale);\r\n const dateFormat = t('dateFormat.full');\r\n selectedDateText.value = dateObj.format(dateFormat);\r\n \r\n showCalendar.value = false; // 选择日期后隐藏日期选择器\r\n };\r\n\r\n // 选择日期\r\n const selectDay = (days: { value: number | string; disabled?: boolean }) => {\r\n if (days.disabled || days.value === '') {\r\n return;\r\n }\r\n resetSelectDate(Number(days.value));\r\n const { year, month, day, week, weekStr, monthStr } = selectDate.value;\r\n emit('confirm', {\r\n date: `${year}-${keepDoubleDigit(month)}-${keepDoubleDigit(day)}`,\r\n year,\r\n month,\r\n week,\r\n monthStr,\r\n weekStr,\r\n day,\r\n });\r\n };\r\n\r\n // 选择月份\r\n const selectMonth = (value: number) => {\r\n showYear.value = false;\r\n showMonth.value = false;\r\n showDate.value.month = value;\r\n let type: 'copyMinDate' | 'copyMaxDate' | undefined;\r\n // 当月份在最小值之外,日期换成最小值日期 或者 当月份在最大值之外,日期换成最大值日期\r\n if (isMinLimitMonth()) {\r\n type = 'copyMinDate';\r\n } else if (isMaxLimitMonth()) {\r\n type = 'copyMaxDate';\r\n }\r\n if (type) {\r\n showDate.value.day = type === 'copyMinDate' ? copyMinDate.value.day : copyMaxDate.value.day;\r\n resetSelectDate(showDate.value.day);\r\n return;\r\n }\r\n let dayValue = selectDate.value.day;\r\n // 判断日是最大值,防止另一个月没有这个日期\r\n const daysInMonth = new Date(showDate.value.year, showDate.value.month + 1, 0).getDate();\r\n dayValue = Math.min(dayValue, daysInMonth); // 确保日期不超过当前月份的最大天数\r\n resetSelectDate(dayValue);\r\n };\r\n\r\n // 选择年份\r\n const selectYear = (value: number) => {\r\n showYear.value = false;\r\n showMonth.value = false;\r\n showDate.value.year = value;\r\n let type: 'copyMinDate' | 'copyMaxDate' | undefined;\r\n // 当日期在最小值之外,月份换成最小值月份 或者 当日期在最大值之外,月份换成最大值月份\r\n if (isMinLimitMonth()) {\r\n type = 'copyMinDate';\r\n } else if (isMaxLimitMonth()) {\r\n type = 'copyMaxDate';\r\n }\r\n if (type) {\r\n showDate.value.month = type === 'copyMinDate' ? copyMinDate.value.month : copyMaxDate.value.month;\r\n showDate.value.day = type === 'copyMinDate' ? copyMinDate.value.day : copyMaxDate.value.day;\r\n resetSelectDate(showDate.value.day);\r\n return;\r\n }\r\n let dayValue = selectDate.value.day;\r\n // 判断日是最大值,防止另一个月没有这个日期\r\n const months = new Date(showDate.value.year, showDate.value.month, 0).getDate();\r\n dayValue = Math.min(dayValue, months); // 确保日期不超过当前月份的最大天数\r\n resetSelectDate(dayValue);\r\n };\r\n\r\n // 判断是否为最小月份限制\r\n const isMinLimitMonth = () => {\r\n return showDate.value.year <= copyMinDate.value.year && showDate.value.month <= copyMinDate.value.month;\r\n };\r\n\r\n // 判断是否为最大月份限制\r\n const isMaxLimitMonth = () => {\r\n return showDate.value.year >= copyMaxDate.value.year && showDate.value.month >= copyMaxDate.value.month;\r\n };\r\n\r\n // 打开年份列表\r\n const openYearList = () => {\r\n if (showYear.value) {\r\n showYear.value = false;\r\n showMonth.value = false;\r\n return;\r\n }\r\n const index = yearList.value.indexOf(selectDate.value.year);\r\n showYear.value = true;\r\n showMonth.value = false;\r\n setTimeout(() => {\r\n if (yearListRef.value) {\r\n yearListRef.value.scrollTop = (index - 3) * 40;\r\n }\r\n });\r\n };\r\n\r\n // 打开月份列表\r\n const openMonthList = () => {\r\n if (showMonth.value) {\r\n showYear.value = false;\r\n showMonth.value = false;\r\n return;\r\n }\r\n const index = monthList.value.indexOf(selectDate.value.month);\r\n showMonth.value = true;\r\n showYear.value = false;\r\n setTimeout(() => {\r\n if (monthListRef.value) {\r\n monthListRef.value.scrollTop = (index - 3) * 20;\r\n }\r\n });\r\n };\r\n\r\n const openCalendarList = () => {\r\n showYear.value = false;\r\n };\r\n\r\n // 清除日期的方法\r\n const clearDate = () => {\r\n selectedDateText.value = '';\r\n showCalendar.value = false;\r\n };\r\n\r\n // 点击文本框和日期组件以外的区域,隐藏日期组件\r\n const handleClickOutside = (event: MouseEvent) => {\r\n if (\r\n calendarRef.value &&\r\n inputRef.value &&\r\n !calendarRef.value.contains(event.target as Node) &&\r\n !inputRef.value.contains(event.target as Node)\r\n ) {\r\n showCalendar.value = false;\r\n }\r\n };\r\n\r\n const isYearHovered = (index: number) => hoveredYearIndex.value === index;\r\n const isMonthHovered = (index: number) => hoveredMonthIndex.value === index;\r\n const isDayHovered = (col: number, row: number) =>\r\n hoveredDayIndex.value.row === row && hoveredDayIndex.value.col === col;\r\n\r\n const handleYearMouseEnter = (index: number) => {\r\n hoveredYearIndex.value = index;\r\n };\r\n\r\n const handleYearMouseLeave = () => {\r\n hoveredYearIndex.value = -1;\r\n };\r\n\r\n const handleMonthMouseEnter = (index: number) => {\r\n hoveredMonthIndex.value = index;\r\n };\r\n\r\n const handleMonthMouseLeave = () => {\r\n hoveredMonthIndex.value = -1;\r\n };\r\n\r\n const handleDayMouseEnter = (col: number, row: number) => {\r\n hoveredDayIndex.value = { row, col };\r\n };\r\n\r\n const handleDayMouseLeave = () => {\r\n hoveredDayIndex.value = { row: -1, col: -1 };\r\n };\r\n\r\n onMounted(() => {\r\n document.addEventListener('click', handleClickOutside);\r\n });\r\n\r\n onUnmounted(() => {\r\n document.removeEventListener('click', handleClickOutside);\r\n });\r\n\r\n return {\r\n t,\r\n locale,\r\n getWeekNames,\r\n getMonthName,\r\n selectDate,\r\n showDate,\r\n copyMinDate,\r\n copyMaxDate,\r\n fadeXType,\r\n nextMonthClick,\r\n prevMonthClick,\r\n keepDoubleDigit,\r\n showYear,\r\n yearList,\r\n rows,\r\n yearListRef,\r\n prevMonth,\r\n nextMonth,\r\n selectDay,\r\n selectYear,\r\n openYearList,\r\n openMonthList,\r\n monthList,\r\n monthListRef,\r\n showMonth,\r\n selectMonth,\r\n openCalendarList,\r\n showCalendar,\r\n selectedDateText,\r\n clearDate,\r\n calendarRef,\r\n inputRef,\r\n isYearHovered,\r\n isMonthHovered,\r\n isDayHovered,\r\n handleYearMouseEnter,\r\n handleYearMouseLeave,\r\n handleMonthMouseEnter,\r\n handleMonthMouseLeave,\r\n handleDayMouseEnter,\r\n handleDayMouseLeave\r\n };\r\n },\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.e-calendar {\r\n background: var(--bg-content);\r\n width: 310px;\r\n border: 1px solid var(--border);\r\n box-shadow: var(--shadow-outset);\r\n z-index: 999;\r\n position: absolute;\r\n font-family: var(--font-family);\r\n}\r\n\r\n.e-date-select {\r\n background: var(--bg-active);\r\n padding: 12px 20px;\r\n color: var(--text-white);\r\n font-weight: 600;\r\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);\r\n box-shadow: var(--shadow-active);\r\n}\r\n\r\n.date-picker-input-wrapper {\r\n position: relative;\r\n display: inline-block;\r\n}\r\n\r\n.date-picker-input {\r\n width: 200px;\r\n padding: 8px 12px;\r\n background: var(--bg-content);\r\n border: 1px solid var(--border);\r\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\r\n font-family: inherit;\r\n font-size: 14px;\r\n color: var(--text-primary);\r\n transition: all var(--transition-fast);\r\n border-radius: 0;\r\n}\r\n\r\n.date-picker-input:focus {\r\n outline: none;\r\n border-color: var(--primary);\r\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 120, 212, 0.2);\r\n}\r\n\r\n.clear-date-button {\r\n position: absolute;\r\n right: 8px;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n cursor: pointer;\r\n width: 24px;\r\n height: 24px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: var(--bg-metal-normal);\r\n border: 1px solid var(--border);\r\n transition: all var(--transition-fast);\r\n}\r\n\r\n.clear-date-button:hover {\r\n background: var(--bg-metal-light);\r\n}\r\n\r\n.clear-date-button svg {\r\n fill: var(--text-secondary);\r\n width: 14px;\r\n height: 14px;\r\n transition: fill var(--transition-fast);\r\n}\r\n\r\n.clear-date-button:hover svg {\r\n fill: var(--text-primary);\r\n}\r\n\r\n.e-date-year {\r\n font-size: 18px;\r\n padding-bottom: 4px;\r\n position: relative;\r\n width: 66px;\r\n height: 25px;\r\n overflow: hidden;\r\n cursor: pointer;\r\n}\r\n\r\n.e-date-year-select {\r\n position: absolute;\r\n opacity: 0.7;\r\n font-size: 20px;\r\n}\r\n\r\n.e-date-year-select.active {\r\n opacity: 1;\r\n}\r\n\r\n.e-date-monthday {\r\n font-size: 26px;\r\n position: relative;\r\n width: 100%;\r\n height: 36px;\r\n overflow: hidden;\r\n}\r\n\r\n.e-date-monthday-select {\r\n position: absolute;\r\n opacity: 0.7;\r\n}\r\n\r\n.e-date-monthday-select.active {\r\n opacity: 1;\r\n}\r\n\r\n.e-calendar-toolbar {\r\n margin: 8px;\r\n height: 40px;\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n background: var(--bg-metal-normal);\r\n border: 1px solid var(--border);\r\n box-shadow: var(--shadow-inset);\r\n padding: 0 8px;\r\n}\r\n\r\n.e-calendar-toolbar-title {\r\n position: relative;\r\n width: 120px;\r\n height: 22px;\r\n text-align: center;\r\n}\r\n\r\n.e-calendar-toolbar-title-content {\r\n position: absolute;\r\n width: 100%;\r\n font-size: 14px;\r\n font-weight: 600;\r\n color: var(--text-primary);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.e-calendar-svg {\r\n padding: 6px;\r\n position: relative;\r\n height: 32px;\r\n width: 32px;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n background: var(--bg-metal-normal);\r\n border: 1px solid var(--border);\r\n cursor: pointer;\r\n transition: all var(--transition-fast);\r\n}\r\n\r\n.e-calendar-svg:hover {\r\n background: var(--bg-metal-light);\r\n}\r\n\r\n.e-calendar-svg:active {\r\n background: var(--bg-metal-pressed);\r\n}\r\n\r\n.e-calendar-svg-icon {\r\n display: block;\r\n fill: var(--text-secondary);\r\n height: 20px;\r\n width: 20px;\r\n user-select: none;\r\n position: relative;\r\n z-index: 2;\r\n transition: fill var(--transition-fast);\r\n}\r\n\r\n.e-calendar-svg:hover .e-calendar-svg-icon {\r\n fill: var(--text-primary);\r\n}\r\n\r\n.e-calendar-svg-cover {\r\n position: absolute;\r\n left: 0;\r\n top: 0;\r\n z-index: 1;\r\n width: 100%;\r\n height: 100%;\r\n background: var(--bg-metal-pressed);\r\n opacity: 0;\r\n display: inline-block;\r\n transition: opacity var(--transition-fast);\r\n}\r\n\r\n.e-calendar-week {\r\n width: 100%;\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--text-secondary);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n height: 24px;\r\n background: var(--bg-metal-dark);\r\n border-top: 1px solid var(--border);\r\n border-bottom: 1px solid var(--border);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.e-calendar-week-day {\r\n flex: 1;\r\n text-align: center;\r\n}\r\n\r\n.e-calendar-monthday {\r\n padding-top: 10px;\r\n font-size: 14px;\r\n position: relative;\r\n width: 100%;\r\n min-height: 220px;\r\n overflow: hidden;\r\n}\r\n\r\n.e-calendar-monthday-wrapper {\r\n position: absolute;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.e-calendar-monthday-row {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}\r\n\r\n.e-calendar-monthday-row-day {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n flex: 1;\r\n position: relative;\r\n height: 35px;\r\n border: 1px solid transparent;\r\n transition: all var(--transition-fast);\r\n}\r\n\r\n.e-calendar-monthday-row-day.pointer {\r\n cursor: pointer;\r\n background: var(--bg-metal-light);\r\n border-color: var(--border);\r\n}\r\n\r\n.e-calendar-monthday-row-day.pointer:hover {\r\n background: var(--bg-metal-normal);\r\n border-color: var(--primary);\r\n}\r\n\r\n.e-calendar-monthday-row-day.active {\r\n color: var(--text-white);\r\n background: var(--bg-active);\r\n border-color: var(--primary-dark);\r\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.e-calendar-monthday-row-day.disabled {\r\n opacity: 0.4;\r\n cursor: not-allowed;\r\n background: var(--bg-metal-dark);\r\n}\r\n\r\n.e-calendar-monthday-row-day-value {\r\n position: relative;\r\n z-index: 1;\r\n font-size: 13px;\r\n font-weight: 500;\r\n}\r\n\r\n.e-calendar-monthday-row-day-cover {\r\n width: 100%;\r\n height: 100%;\r\n background: var(--bg-active);\r\n position: absolute;\r\n left: 0;\r\n top: 0;\r\n transform: translate3d(0, 0, 0);\r\n z-index: 0;\r\n opacity: 1;\r\n display: block;\r\n}\r\n\r\n.e-calendar-monthday-row-day.hover {\r\n background: var(--bg-metal-normal);\r\n border-color: var(--primary);\r\n}\r\n\r\n.e-calendar-year {\r\n height: 276px;\r\n overflow: auto;\r\n background: var(--bg-content);\r\n border-top: 1px solid var(--border);\r\n}\r\n\r\n.e-calendar-year li {\r\n padding: 12px;\r\n text-align: center;\r\n font-size: 14px;\r\n font-weight: 500;\r\n cursor: pointer;\r\n border-bottom: 1px solid var(--border);\r\n background: var(--bg-metal-light);\r\n color: var(--text-primary);\r\n transition: all var(--transition-fast);\r\n}\r\n\r\n.e-calendar-year li:hover {\r\n background: var(--bg-metal-normal);\r\n color: var(--primary);\r\n}\r\n\r\n.e-calendar-year li.active {\r\n color: var(--text-white);\r\n background: var(--bg-active);\r\n font-weight: 700;\r\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.e-calendar-year li.hover {\r\n background: var(--bg-metal-normal);\r\n}\r\n\r\n.fadeX_Prev-enter-active,\r\n.fadeX_Prev-leave-active,\r\n.fadeX_Next-enter-active,\r\n.fadeX_Next-leave-active,\r\n.fadeY-enter-active,\r\n.fadeY-leave-active {\r\n transition: all 0.5s;\r\n}\r\n\r\n.fadeX_Prev-enter {\r\n transform: translateX(-100px);\r\n opacity: 0;\r\n}\r\n\r\n.fadeX_Prev-leave-active {\r\n transform: translateX(100px);\r\n opacity: 0;\r\n}\r\n\r\n.fadeX_Next-enter {\r\n transform: translateX(100px);\r\n opacity: 0;\r\n}\r\n\r\n.fadeX_Next-leave-active {\r\n transform: translateX(-100px);\r\n opacity: 0;\r\n}\r\n\r\n.fadeY-enter {\r\n transform: translateY(30px);\r\n opacity: 0;\r\n}\r\n\r\n.fadeY-leave-active {\r\n transform: translateY(-30px);\r\n opacity: 0;\r\n}\r\n\r\n.e_calendar_svg_btn-enter-active,\r\n.e_calendar_svg_btn-leave-active {\r\n transition: all 1s;\r\n}\r\n\r\n.e_calendar_svg_btn-enter {\r\n opacity: 1;\r\n}\r\n\r\n.e_calendar_day-enter-active {\r\n transition: all 0.2s;\r\n}\r\n\r\n.e_calendar_svg_btn-leave-active,\r\n.e_calendar_day-enter {\r\n opacity: 0;\r\n}\r\n\r\n.e_calendar_day-enter {\r\n width: 0;\r\n height: 0;\r\n transform: translate3d(12px, 12px, 0);\r\n}\r\n</style>","<template>\r\n <div>\r\n <!-- 添加文本框 -->\r\n <div class=\"date-picker-input-wrapper\">\r\n <input type=\"text\" v-model=\"selectedDateText\" @click=\"showCalendar = true\" readonly :placeholder=\"t('datePicker.selectDate')\"\r\n class=\"date-picker-input\" ref=\"inputRef\" />\r\n <span class=\"clear-date-button\" @click=\"clearDate\" v-if=\"selectedDateText\">\r\n <svg viewBox=\"0 0 24 24\">\r\n <path\r\n d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\">\r\n </path>\r\n </svg>\r\n </span>\r\n </div>\r\n <!-- 日期选择器 -->\r\n <div class=\"e-calendar\" v-show=\"showCalendar\" ref=\"calendarRef\">\r\n <div class=\"e-date-select\">\r\n <div class=\"e-date-year\">\r\n <transition name=\"fadeY\">\r\n <div :key=\"selectDate.year\" class=\"e-date-year-select\" @click=\"openYearList\" :class=\"{ active: showYear }\">\r\n {{ selectDate.year }}\r\n </div>\r\n </transition>\r\n </div>\r\n <div class=\"e-date-monthday\">\r\n <transition name=\"fadeY\">\r\n <div :key=\"selectDate.day\" class=\"e-date-monthday-select\" :class=\"{ active: !showYear }\"\r\n @click=\"openCalendarList\">\r\n <span>{{ keepDoubleDigit(selectDate.month) }}-{{ keepDoubleDigit(selectDate.day) }}</span>&nbsp;\r\n <span style=\"cursor: pointer;\" @click=\"openMonthList\">{{ showDate.monthStr }}</span>\r\n </div>\r\n </transition>\r\n </div>\r\n </div>\r\n\r\n <div class=\"e-calendar-container\" v-show=\"!showYear && !showMonth\">\r\n <div class=\"e-calendar-toolbar\">\r\n <div class=\"e-calendar-svg\" @click=\"prevMonth\">\r\n <svg viewBox=\"0 0 24 24\" class=\"e-calendar-svg-icon\">\r\n <path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\"></path>\r\n </svg>\r\n <transition name=\"e_calendar_svg_btn\">\r\n <div class=\"e-calendar-svg-cover\" v-if=\"prevMonthClick\"></div>\r\n </transition>\r\n </div>\r\n <div class=\"e-calendar-toolbar-title\">\r\n <transition :name=\"fadeXType\">\r\n <div :key=\"showDate.monthStr\" class=\"e-calendar-toolbar-title-content\">\r\n <strong>{{ showDate.year }}</strong>&nbsp;\r\n <span style=\"cursor: pointer;\" @click=\"openMonthList\">{{ showDate.monthStr }}</span>\r\n </div>\r\n </transition>\r\n </div>\r\n <div class=\"e-calendar-svg\" @click=\"nextMonth\">\r\n <svg viewBox=\"0 0 24 24\" class=\"e-calendar-svg-icon\">\r\n <path d=\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\"></path>\r\n </svg>\r\n <transition name=\"e_calendar_svg_btn\">\r\n <div class=\"e-calendar-svg-cover\" v-if=\"nextMonthClick\"></div>\r\n </transition>\r\n </div>\r\n </div>\r\n <div class=\"e-calendar-week\">\r\n <span v-for=\"(weekName, index) in getWeekNames\" :key=\"index\" class=\"e-calendar-week-day\">{{ weekName }}</span>\r\n </div>\r\n <div class=\"e-calendar-monthday\">\r\n <transition :name=\"fadeXType\">\r\n <div :key=\"showDate.monthStr\" class=\"e-calendar-monthday-wrapper\">\r\n <div v-for=\"(row, index) in rows\" :key=\"index\" class=\"e-calendar-monthday-row\">\r\n <span v-for=\"(day, dayIndex) in row\" :key=\"dayIndex\" class=\"e-calendar-monthday-row-day\"\r\n @click=\"selectDay(day)\" @mouseenter=\"handleDayMouseEnter(dayIndex, index)\"\r\n @mouseleave=\"handleDayMouseLeave()\" :class=\"{\r\n active: day.selected,\r\n disabled: day.disabled,\r\n pointer: day.value !== '',\r\n hover: isDayHovered(dayIndex, index)\r\n }\">\r\n <span v-text=\"day.value\" class=\"e-calendar-monthday-row-day-value\"></span>\r\n <transition name=\"e_calendar_day\">\r\n <span class=\"e-calendar-monthday-row-day-cover\" v-if=\"day.selected\"></span>\r\n </transition>\r\n </span>\r\n </div>\r\n </div>\r\n </transition>\r\n </div>\r\n </div>\r\n <ul class=\"e-calendar-year\" v-show=\"showYear\" ref=\"yearList\">\r\n <li v-for=\"(item, index) in yearList\" :key=\"index\" :class=\"{\r\n active: item === selectDate.year,\r\n hover: isYearHovered(index)\r\n }\" @click=\"selectYear(item)\" @mouseenter=\"handleYearMouseEnter(index)\" @mouseleave=\"handleYearMouseLeave()\">\r\n {{ item }}\r\n </li>\r\n </ul>\r\n <ul class=\"e-calendar-year\" v-show=\"showMonth\" ref=\"monthList\">\r\n <li v-for=\"(item, index) in monthList\" :key=\"index\" :class=\"{\r\n active: item === selectDate.month,\r\n hover: isMonthHovered(index)\r\n }\" @click=\"selectMonth(item)\" @mouseenter=\"handleMonthMouseEnter(index)\" @mouseleave=\"handleMonthMouseLeave()\">\r\n {{ getMonthName(item) }}\r\n </li>\r\n </ul>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, computed, onBeforeMount, onMounted, onUnmounted, watchEffect, watch } from 'vue';\r\nimport { useI18n } from './i18n';\r\nimport dayjs from 'dayjs';\r\nimport 'dayjs/locale/zh-cn';\r\nimport 'dayjs/locale/en';\r\nimport 'dayjs/locale/ja';\r\nimport 'dayjs/locale/ko';\r\nimport 'dayjs/locale/fr';\r\nimport 'dayjs/locale/de';\r\nimport 'dayjs/locale/es';\r\nimport 'dayjs/locale/ru';\r\n\r\nexport default defineComponent({\r\n props: {\r\n // 打开date picker的初始值,必传,格式是(2017-08-11)\r\n date: {\r\n type: String,\r\n required: true,\r\n },\r\n // 日期最小值\r\n minDate: String,\r\n // 日期最大值\r\n maxDate: String,\r\n },\r\n setup(props, { emit }) {\r\n const { t, locale } = useI18n();\r\n \r\n // 获取 dayjs locale\r\n const getDayjsLocale = () => {\r\n const localeMap: Record<string, string> = {\r\n 'zh-CN': 'zh-cn',\r\n 'en-US': 'en',\r\n 'ja-JP': 'ja',\r\n 'ko-KR': 'ko',\r\n 'fr-FR': 'fr',\r\n 'de-DE': 'de',\r\n 'es-ES': 'es',\r\n 'ru-RU': 'ru'\r\n };\r\n return localeMap[locale.value] || 'en';\r\n };\r\n \r\n // 获取星期名称\r\n const getWeekNames = computed(() => {\r\n const dayjsLocale = getDayjsLocale();\r\n const weekNames = [];\r\n for (let i = 1; i <= 7; i++) {\r\n // 使用 dayjs 获取本地化的星期名称(简写)\r\n const day = dayjs().day(i).locale(dayjsLocale);\r\n weekNames.push(day.format('dd'));\r\n }\r\n return weekNames;\r\n });\r\n \r\n // 获取月份名称\r\n const getMonthName = (month: number) => {\r\n const dayjsLocale = getDayjsLocale();\r\n return dayjs().month(month - 1).locale(dayjsLocale).format('MMMM');\r\n };\r\n \r\n // 定义响应式数据\r\n let selectDate = ref({\r\n year: 0,\r\n month: 0,\r\n day: 0,\r\n week: 0,\r\n date: '',\r\n weekStr: '',\r\n monthStr: '',\r\n });\r\n let showDate = ref({\r\n year: 0,\r\n month: 0,\r\n day: 0,\r\n week: 0,\r\n date: '',\r\n monthStr: '',\r\n weekStr: '',\r\n });\r\n let copyMinDate = ref({\r\n year: 0,\r\n month: 0,\r\n day: 0,\r\n });\r\n let copyMaxDate = ref({\r\n year: 0,\r\n month: 0,\r\n day: 0,\r\n });\r\n\r\n const fadeXType = ref('fadeX_Prev');\r\n const nextMonthClick = ref(false);\r\n const prevMonthClick = ref(false);\r\n const showYear = ref(false);\r\n const showMonth = ref(false);\r\n const yearListRef = ref<HTMLUListElement | null>(null);\r\n const monthListRef = ref<HTMLUListElement | null>(null);\r\n const showCalendar = ref(false); // 控制日期选择器的显示和隐藏\r\n const selectedDateText = ref(''); // 文本框显示的日期\r\n const calendarRef = ref<HTMLDivElement | null>(null); // 日期组件的引用\r\n const inputRef = ref<HTMLInputElement | null>(null); // 新增文本框引用\r\n\r\n const hoveredYearIndex = ref(-1);\r\n const hoveredMonthIndex = ref(-1);\r\n const hoveredDayIndex = ref({ row: -1, col: -1 });\r\n\r\n const yearList = computed(() => {\r\n const result: number[] = [];\r\n for (let i = copyMinDate.value.year; i <= copyMaxDate.value.year; i += 1) {\r\n result.push(i);\r\n }\r\n return result;\r\n });\r\n\r\n const monthList = computed(() => {\r\n const result: number[] = [];\r\n for (let i = 1; i <= 12; i += 1) {\r\n result.push(i);\r\n }\r\n return result;\r\n });\r\n\r\n const rows = computed(() => {\r\n const { year, month } = showDate.value;\r\n const months = new Date(year, month, 0).getDate();\r\n const result: {\r\n value: number | string;\r\n selected?: boolean;\r\n disabled?: boolean;\r\n }[][] = [];\r\n let row: {\r\n value: number | string;\r\n selected?: boolean;\r\n disabled?: boolean;\r\n }[] = [];\r\n let weekValue: number;\r\n // 按照星期分组\r\n for (let i = 1; i <= months; i += 1) {\r\n // 根据日期获取星期,并让开头是1,而非0\r\n weekValue = new Date(year, month - 1, i).getDay() + 1;\r\n // 判断月第一天在星期几,并填充前面的空白区域\r\n if (i === 1 && weekValue !== 1) {\r\n addRowEmptyValue(row, weekValue);\r\n addRowDayValue(row, i);\r\n } else {\r\n addRowDayValue(row, i);\r\n // 判断月最后一天在星期几,并填充后面的空白区域\r\n if (i === months && weekValue !== 7) {\r\n addRowEmptyValue(row, (7 - weekValue) + 1);\r\n }\r\n }\r\n // 按照一周分组\r\n if (weekValue % 7 === 0 || i === months) {\r\n result.push(row);\r\n row = [];\r\n }\r\n }\r\n showDate.value.monthStr = getMonthName(showDate.value.month);\r\n return result;\r\n });\r\n\r\n // 定义函数,接收一个 number 类型的参数,返回一个 string 类型的值\r\n const keepDoubleDigit = (number: number): string => {\r\n // 判断传入的数字是否大于 9\r\n return number > 9 ? String(number) : `0${number}`;\r\n };\r\n\r\n // 拆分日期\r\n const splitDate = (date: string, addStr = false) => {\r\n let result: {\r\n year: number;\r\n month: number;\r\n day: number;\r\n week?: number;\r\n monthStr?: string;\r\n weekStr?: string;\r\n } = {\r\n year: 0,\r\n month: 0,\r\n day: 0,\r\n };\r\n const splitValue = date.split('-');\r\n try {\r\n if (!splitValue || splitValue.length < 3) {\r\n throw new Error('时间格式不正确');\r\n }\r\n result = {\r\n year: Number(splitValue[0]),\r\n month: Number(splitValue[1]),\r\n day: Number(splitValue[2]),\r\n };\r\n if (addStr) {\r\n result.week = new Date(result.year, result.month - 1, result.day).getDay() + 1;\r\n result.monthStr = getMonthName(result.month);\r\n const dayjsLocale = getDayjsLocale();\r\n result.weekStr = dayjs().day(result.week).locale(dayjsLocale).format('dddd');\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n return result;\r\n };\r\n\r\n // 初始化日期选择器\r\n const initDatePicker = () => {\r\n const splitResult = splitDate(props.date, true);\r\n showDate.value = {\r\n ...splitResult,\r\n date: `${splitResult.year}-${keepDoubleDigit(splitResult.month)}-${keepDoubleDigit(\r\n splitResult.day\r\n )}`,\r\n week: splitResult.week || 0,\r\n monthStr: splitResult.monthStr || '',\r\n weekStr: splitResult.weekStr || '',\r\n };\r\n copyMinDate.value = { ...splitDate(props.minDate || '1970-01-01') };\r\n copyMaxDate.value = { ...splitDate(props.maxDate || '2099-12-31') };\r\n selectDate.value = { ...showDate.value };\r\n \r\n // 使用国际化日期格式\r\n const dayjsLocale = getDayjsLocale();\r\n const dateObj = dayjs(showDate.value.date).locale(dayjsLocale);\r\n const dateFormat = t('dateFormat.full');\r\n selectedDateText.value = dateObj.format(dateFormat);\r\n };\r\n\r\n watchEffect(() => {\r\n if (props.date) {\r\n initDatePicker(); // 当 props.date 变化时重新初始化日期选择器\r\n }\r\n });\r\n\r\n onBeforeMount(() => {\r\n initDatePicker();\r\n });\r\n\r\n // 添加空值\r\n const addRowEmptyValue = (\r\n row: {\r\n value: number | string;\r\n selected?: boolean;\r\n disabled?: boolean;\r\n }[],\r\n count: number\r\n ) => {\r\n for (let w = 1; w < count; w += 1) {\r\n row.push({\r\n value: '',\r\n });\r\n }\r\n };\r\n\r\n // 添加日期值\r\n const addRowDayValue = (\r\n row: {\r\n value: number | string;\r\n selected?: boolean;\r\n disabled?: boolean;\r\n }[],\r\n i: number\r\n ) => {\r\n const value = { value: i };\r\n const { day, month, year } = selectDate.value;\r\n const showDateValue = showDate.value;\r\n // 判断已经选择的\r\n if (year === showDateValue.year && month === showDateValue.month && day === i) {\r\n // 为对象添加 selected 属性\r\n const newValue = { ...value, selected: true };\r\n row.push(newValue);\r\n return; // 添加 return 避免重复添加\r\n }\r\n // 当日期在最小值之外,禁止点击\r\n if (isMinLimitMonth() && i < copyMinDate.value.day) {\r\n // 修复:添加 disabled 属性\r\n const newValue = { ...value, disabled: true };\r\n row.push(newValue);\r\n return; // 添加 return 避免重复添加\r\n }\r\n // 当日期在最大值之外,禁止点击\r\n if (isMaxLimitMonth() && i > copyMaxDate.value.day) {\r\n // 修复:添加 disabled 属性\r\n const newValue = { ...value, disabled: true };\r\n row.push(newValue);\r\n return; // 添加 return 避免重复添加\r\n }\r\n row.push(value);\r\n };\r\n\r\n // 切换到上一个月\r\n const prevMonth = () => {\r\n if (prevMonthClick.value) {\r\n return;\r\n }\r\n prevMonthClick.value = true;\r\n setTimeout(() => {\r\n prevMonthClick.value = false;\r\n }, 500);\r\n fadeXType.value = 'fadeX_Prev';\r\n // 如何当前月份已经小于等于minMonth 就不让其在执行\r\n if (isMinLimitMonth()) {\r\n return;\r\n }\r\n const { year, month } = showDate.value;\r\n // 判断当前月份,如果已经等于1(1就是一月,而不是二月)\r\n if (month <= 1) {\r\n showDate.value.year = year - 1;\r\n showDate.value.month = 12;\r\n } else {\r\n showDate.value.month -= 1;\r\n }\r\n };\r\n\r\n // 切换到下一个月\r\n const nextMonth = () => {\r\n if (nextMonthClick.value) {\r\n return;\r\n }\r\n nextMonthClick.value = true;\r\n setTimeout(() => {\r\n nextMonthClick.value = false;\r\n }, 500);\r\n fadeXType.value = 'fadeX_Next';\r\n // 如何当前月份已经大于等于maxMonth 就不让其在执行\r\n if (isMaxLimitMonth()) {\r\n return;\r\n }\r\n const { year, month } = showDate.value;\r\n // 判断当前月份,如果已经等于12(12就是十二月)\r\n if (month >= 12) {\r\n showDate.value.year = year + 1;\r\n showDate.value.month = 1;\r\n } else {\r\n showDate.value.month += 1;\r\n }\r\n };\r\n\r\n // 重置选择日期\r\n let resetSelectDate = (dayValue: number) => {\r\n // 修改为逐个修改属性\r\n selectDate.value.year = showDate.value.year;\r\n selectDate.value.month = showDate.value.month;\r\n selectDate.value.week = showDate.value.week;\r\n selectDate.value.date = showDate.value.date;\r\n selectDate.value.weekStr = showDate.value.weekStr;\r\n selectDate.value.monthStr = showDate.value.monthStr;\r\n selectDate.value.day = dayValue;\r\n selectDate.value.week = new Date(showDate.value.year, showDate.value.month - 1, dayValue).getDay() + 1;\r\n const dayjsLocale = getDayjsLocale();\r\n selectDate.value.weekStr = dayjs().day(selectDate.value.week).locale(dayjsLocale).format('dddd');\r\n \r\n // 使用国际化日期格式\r\n const dateObj = dayjs(`${selectDate.value.year}-${keepDoubleDigit(selectDate.value.month)}-${keepDoubleDigit(selectDate.value.day)}`).locale(dayjsLocale);\r\n const dateFormat = t('dateFormat.full');\r\n selectedDateText.value = dateObj.format(dateFormat);\r\n \r\n showCalendar.value = false; // 选择日期后隐藏日期选择器\r\n };\r\n\r\n // 选择日期\r\n const selectDay = (days: { value: number | string; disabled?: boolean }) => {\r\n if (days.disabled || days.value === '') {\r\n return;\r\n }\r\n resetSelectDate(Number(days.value));\r\n const { year, month, day, week, weekStr, monthStr } = selectDate.value;\r\n emit('confirm', {\r\n date: `${year}-${keepDoubleDigit(month)}-${keepDoubleDigit(day)}`,\r\n year,\r\n month,\r\n week,\r\n monthStr,\r\n weekStr,\r\n day,\r\n });\r\n };\r\n\r\n // 选择月份\r\n const selectMonth = (value: number) => {\r\n showYear.value = false;\r\n showMonth.value = false;\r\n showDate.value.month = value;\r\n let type: 'copyMinDate' | 'copyMaxDate' | undefined;\r\n // 当月份在最小值之外,日期换成最小值日期 或者 当月份在最大值之外,日期换成最大值日期\r\n if (isMinLimitMonth()) {\r\n type = 'copyMinDate';\r\n } else if (isMaxLimitMonth()) {\r\n type = 'copyMaxDate';\r\n }\r\n if (type) {\r\n showDate.value.day = type === 'copyMinDate' ? copyMinDate.value.day : copyMaxDate.value.day;\r\n resetSelectDate(showDate.value.day);\r\n return;\r\n }\r\n let dayValue = selectDate.value.day;\r\n // 判断日是最大值,防止另一个月没有这个日期\r\n const daysInMonth = new Date(showDate.value.year, showDate.value.month + 1, 0).getDate();\r\n dayValue = Math.min(dayValue, daysInMonth); // 确保日期不超过当前月份的最大天数\r\n resetSelectDate(dayValue);\r\n };\r\n\r\n // 选择年份\r\n const selectYear = (value: number) => {\r\n showYear.value = false;\r\n showMonth.value = false;\r\n showDate.value.year = value;\r\n let type: 'copyMinDate' | 'copyMaxDate' | undefined;\r\n // 当日期在最小值之外,月份换成最小值月份 或者 当日期在最大值之外,月份换成最大值月份\r\n if (isMinLimitMonth()) {\r\n type = 'copyMinDate';\r\n } else if (isMaxLimitMonth()) {\r\n type = 'copyMaxDate';\r\n }\r\n if (type) {\r\n showDate.value.month = type === 'copyMinDate' ? copyMinDate.value.month : copyMaxDate.value.month;\r\n showDate.value.day = type === 'copyMinDate' ? copyMinDate.value.day : copyMaxDate.value.day;\r\n resetSelectDate(showDate.value.day);\r\n return;\r\n }\r\n let dayValue = selectDate.value.day;\r\n // 判断日是最大值,防止另一个月没有这个日期\r\n const months = new Date(showDate.value.year, showDate.value.month, 0).getDate();\r\n dayValue = Math.min(dayValue, months); // 确保日期不超过当前月份的最大天数\r\n resetSelectDate(dayValue);\r\n };\r\n\r\n // 判断是否为最小月份限制\r\n const isMinLimitMonth = () => {\r\n return showDate.value.year <= copyMinDate.value.year && showDate.value.month <= copyMinDate.value.month;\r\n };\r\n\r\n // 判断是否为最大月份限制\r\n const isMaxLimitMonth = () => {\r\n return showDate.value.year >= copyMaxDate.value.year && showDate.value.month >= copyMaxDate.value.month;\r\n };\r\n\r\n // 打开年份列表\r\n const openYearList = () => {\r\n if (showYear.value) {\r\n showYear.value = false;\r\n showMonth.value = false;\r\n return;\r\n }\r\n const index = yearList.value.indexOf(selectDate.value.year);\r\n showYear.value = true;\r\n showMonth.value = false;\r\n setTimeout(() => {\r\n if (yearListRef.value) {\r\n yearListRef.value.scrollTop = (index - 3) * 40;\r\n }\r\n });\r\n };\r\n\r\n // 打开月份列表\r\n const openMonthList = () => {\r\n if (showMonth.value) {\r\n showYear.value = false;\r\n showMonth.value = false;\r\n return;\r\n }\r\n const index = monthList.value.indexOf(selectDate.value.month);\r\n showMonth.value = true;\r\n showYear.value = false;\r\n setTimeout(() => {\r\n if (monthListRef.value) {\r\n monthListRef.value.scrollTop = (index - 3) * 20;\r\n }\r\n });\r\n };\r\n\r\n const openCalendarList = () => {\r\n showYear.value = false;\r\n };\r\n\r\n // 清除日期的方法\r\n const clearDate = () => {\r\n selectedDateText.value = '';\r\n showCalendar.value = false;\r\n };\r\n\r\n // 点击文本框和日期组件以外的区域,隐藏日期组件\r\n const handleClickOutside = (event: MouseEvent) => {\r\n if (\r\n calendarRef.value &&\r\n inputRef.value &&\r\n !calendarRef.value.contains(event.target as Node) &&\r\n !inputRef.value.contains(event.target as Node)\r\n ) {\r\n showCalendar.value = false;\r\n }\r\n };\r\n\r\n const isYearHovered = (index: number) => hoveredYearIndex.value === index;\r\n const isMonthHovered = (index: number) => hoveredMonthIndex.value === index;\r\n const isDayHovered = (col: number, row: number) =>\r\n hoveredDayIndex.value.row === row && hoveredDayIndex.value.col === col;\r\n\r\n const handleYearMouseEnter = (index: number) => {\r\n hoveredYearIndex.value = index;\r\n };\r\n\r\n const handleYearMouseLeave = () => {\r\n hoveredYearIndex.value = -1;\r\n };\r\n\r\n const handleMonthMouseEnter = (index: number) => {\r\n hoveredMonthIndex.value = index;\r\n };\r\n\r\n const handleMonthMouseLeave = () => {\r\n hoveredMonthIndex.value = -1;\r\n };\r\n\r\n const handleDayMouseEnter = (col: number, row: number) => {\r\n hoveredDayIndex.value = { row, col };\r\n };\r\n\r\n const handleDayMouseLeave = () => {\r\n hoveredDayIndex.value = { row: -1, col: -1 };\r\n };\r\n\r\n onMounted(() => {\r\n document.addEventListener('click', handleClickOutside);\r\n });\r\n\r\n onUnmounted(() => {\r\n document.removeEventListener('click', handleClickOutside);\r\n });\r\n\r\n return {\r\n t,\r\n locale,\r\n getWeekNames,\r\n getMonthName,\r\n selectDate,\r\n showDate,\r\n copyMinDate,\r\n copyMaxDate,\r\n fadeXType,\r\n nextMonthClick,\r\n prevMonthClick,\r\n keepDoubleDigit,\r\n showYear,\r\n yearList,\r\n rows,\r\n yearListRef,\r\n prevMonth,\r\n nextMonth,\r\n selectDay,\r\n selectYear,\r\n openYearList,\r\n openMonthList,\r\n monthList,\r\n monthListRef,\r\n showMonth,\r\n selectMonth,\r\n openCalendarList,\r\n showCalendar,\r\n selectedDateText,\r\n clearDate,\r\n calendarRef,\r\n inputRef,\r\n isYearHovered,\r\n isMonthHovered,\r\n isDayHovered,\r\n handleYearMouseEnter,\r\n handleYearMouseLeave,\r\n handleMonthMouseEnter,\r\n handleMonthMouseLeave,\r\n handleDayMouseEnter,\r\n handleDayMouseLeave\r\n };\r\n },\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.e-calendar {\r\n background: var(--bg-content);\r\n width: 310px;\r\n border: 1px solid var(--border);\r\n box-shadow: var(--shadow-outset);\r\n z-index: 999;\r\n position: absolute;\r\n font-family: var(--font-family);\r\n}\r\n\r\n.e-date-select {\r\n background: var(--bg-active);\r\n padding: 12px 20px;\r\n color: var(--text-white);\r\n font-weight: 600;\r\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);\r\n box-shadow: var(--shadow-active);\r\n}\r\n\r\n.date-picker-input-wrapper {\r\n position: relative;\r\n display: inline-block;\r\n}\r\n\r\n.date-picker-input {\r\n width: 200px;\r\n padding: 8px 12px;\r\n background: var(--bg-content);\r\n border: 1px solid var(--border);\r\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\r\n font-family: inherit;\r\n font-size: 14px;\r\n color: var(--text-primary);\r\n transition: all var(--transition-fast);\r\n border-radius: 0;\r\n}\r\n\r\n.date-picker-input:focus {\r\n outline: none;\r\n border-color: var(--primary);\r\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 120, 212, 0.2);\r\n}\r\n\r\n.clear-date-button {\r\n position: absolute;\r\n right: 8px;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n cursor: pointer;\r\n width: 24px;\r\n height: 24px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: var(--bg-metal-normal);\r\n border: 1px solid var(--border);\r\n transition: all var(--transition-fast);\r\n}\r\n\r\n.clear-date-button:hover {\r\n background: var(--bg-metal-light);\r\n}\r\n\r\n.clear-date-button svg {\r\n fill: var(--text-secondary);\r\n width: 14px;\r\n height: 14px;\r\n transition: fill var(--transition-fast);\r\n}\r\n\r\n.clear-date-button:hover svg {\r\n fill: var(--text-primary);\r\n}\r\n\r\n.e-date-year {\r\n font-size: 18px;\r\n padding-bottom: 4px;\r\n position: relative;\r\n width: 66px;\r\n height: 25px;\r\n overflow: hidden;\r\n cursor: pointer;\r\n}\r\n\r\n.e-date-year-select {\r\n position: absolute;\r\n opacity: 0.7;\r\n font-size: 20px;\r\n}\r\n\r\n.e-date-year-select.active {\r\n opacity: 1;\r\n}\r\n\r\n.e-date-monthday {\r\n font-size: 26px;\r\n position: relative;\r\n width: 100%;\r\n height: 36px;\r\n overflow: hidden;\r\n}\r\n\r\n.e-date-monthday-select {\r\n position: absolute;\r\n opacity: 0.7;\r\n}\r\n\r\n.e-date-monthday-select.active {\r\n opacity: 1;\r\n}\r\n\r\n.e-calendar-toolbar {\r\n margin: 8px;\r\n height: 40px;\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n background: var(--bg-metal-normal);\r\n border: 1px solid var(--border);\r\n box-shadow: var(--shadow-inset);\r\n padding: 0 8px;\r\n}\r\n\r\n.e-calendar-toolbar-title {\r\n position: relative;\r\n width: 120px;\r\n height: 22px;\r\n text-align: center;\r\n}\r\n\r\n.e-calendar-toolbar-title-content {\r\n position: absolute;\r\n width: 100%;\r\n font-size: 14px;\r\n font-weight: 600;\r\n color: var(--text-primary);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.e-calendar-svg {\r\n padding: 6px;\r\n position: relative;\r\n height: 32px;\r\n width: 32px;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n background: var(--bg-metal-normal);\r\n border: 1px solid var(--border);\r\n cursor: pointer;\r\n transition: all var(--transition-fast);\r\n}\r\n\r\n.e-calendar-svg:hover {\r\n background: var(--bg-metal-light);\r\n}\r\n\r\n.e-calendar-svg:active {\r\n background: var(--bg-metal-pressed);\r\n}\r\n\r\n.e-calendar-svg-icon {\r\n display: block;\r\n fill: var(--text-secondary);\r\n height: 20px;\r\n width: 20px;\r\n user-select: none;\r\n position: relative;\r\n z-index: 2;\r\n transition: fill var(--transition-fast);\r\n}\r\n\r\n.e-calendar-svg:hover .e-calendar-svg-icon {\r\n fill: var(--text-primary);\r\n}\r\n\r\n.e-calendar-svg-cover {\r\n position: absolute;\r\n left: 0;\r\n top: 0;\r\n z-index: 1;\r\n width: 100%;\r\n height: 100%;\r\n background: var(--bg-metal-pressed);\r\n opacity: 0;\r\n display: inline-block;\r\n transition: opacity var(--transition-fast);\r\n}\r\n\r\n.e-calendar-week {\r\n width: 100%;\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--text-secondary);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n height: 24px;\r\n background: var(--bg-metal-dark);\r\n border-top: 1px solid var(--border);\r\n border-bottom: 1px solid var(--border);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.e-calendar-week-day {\r\n flex: 1;\r\n text-align: center;\r\n}\r\n\r\n.e-calendar-monthday {\r\n padding-top: 10px;\r\n font-size: 14px;\r\n position: relative;\r\n width: 100%;\r\n min-height: 220px;\r\n overflow: hidden;\r\n}\r\n\r\n.e-calendar-monthday-wrapper {\r\n position: absolute;\r\n width: 100%;\r\n height: 100%;\r\n}\r\n\r\n.e-calendar-monthday-row {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}\r\n\r\n.e-calendar-monthday-row-day {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n flex: 1;\r\n position: relative;\r\n height: 35px;\r\n border: 1px solid transparent;\r\n transition: all var(--transition-fast);\r\n}\r\n\r\n.e-calendar-monthday-row-day.pointer {\r\n cursor: pointer;\r\n background: var(--bg-metal-light);\r\n border-color: var(--border);\r\n}\r\n\r\n.e-calendar-monthday-row-day.pointer:hover {\r\n background: var(--bg-metal-normal);\r\n border-color: var(--primary);\r\n}\r\n\r\n.e-calendar-monthday-row-day.active {\r\n color: var(--text-white);\r\n background: var(--bg-active);\r\n border-color: var(--primary-dark);\r\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.e-calendar-monthday-row-day.disabled {\r\n opacity: 0.4;\r\n cursor: not-allowed;\r\n background: var(--bg-metal-dark);\r\n}\r\n\r\n.e-calendar-monthday-row-day-value {\r\n position: relative;\r\n z-index: 1;\r\n font-size: 13px;\r\n font-weight: 500;\r\n}\r\n\r\n.e-calendar-monthday-row-day-cover {\r\n width: 100%;\r\n height: 100%;\r\n background: var(--bg-active);\r\n position: absolute;\r\n left: 0;\r\n top: 0;\r\n transform: translate3d(0, 0, 0);\r\n z-index: 0;\r\n opacity: 1;\r\n display: block;\r\n}\r\n\r\n.e-calendar-monthday-row-day.hover {\r\n background: var(--bg-metal-normal);\r\n border-color: var(--primary);\r\n}\r\n\r\n.e-calendar-year {\r\n height: 276px;\r\n overflow: auto;\r\n background: var(--bg-content);\r\n border-top: 1px solid var(--border);\r\n}\r\n\r\n.e-calendar-year li {\r\n padding: 12px;\r\n text-align: center;\r\n font-size: 14px;\r\n font-weight: 500;\r\n cursor: pointer;\r\n border-bottom: 1px solid var(--border);\r\n background: var(--bg-metal-light);\r\n color: var(--text-primary);\r\n transition: all var(--transition-fast);\r\n}\r\n\r\n.e-calendar-year li:hover {\r\n background: var(--bg-metal-normal);\r\n color: var(--primary);\r\n}\r\n\r\n.e-calendar-year li.active {\r\n color: var(--text-white);\r\n background: var(--bg-active);\r\n font-weight: 700;\r\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.e-calendar-year li.hover {\r\n background: var(--bg-metal-normal);\r\n}\r\n\r\n.fadeX_Prev-enter-active,\r\n.fadeX_Prev-leave-active,\r\n.fadeX_Next-enter-active,\r\n.fadeX_Next-leave-active,\r\n.fadeY-enter-active,\r\n.fadeY-leave-active {\r\n transition: all 0.5s;\r\n}\r\n\r\n.fadeX_Prev-enter {\r\n transform: translateX(-100px);\r\n opacity: 0;\r\n}\r\n\r\n.fadeX_Prev-leave-active {\r\n transform: translateX(100px);\r\n opacity: 0;\r\n}\r\n\r\n.fadeX_Next-enter {\r\n transform: translateX(100px);\r\n opacity: 0;\r\n}\r\n\r\n.fadeX_Next-leave-active {\r\n transform: translateX(-100px);\r\n opacity: 0;\r\n}\r\n\r\n.fadeY-enter {\r\n transform: translateY(30px);\r\n opacity: 0;\r\n}\r\n\r\n.fadeY-leave-active {\r\n transform: translateY(-30px);\r\n opacity: 0;\r\n}\r\n\r\n.e_calendar_svg_btn-enter-active,\r\n.e_calendar_svg_btn-leave-active {\r\n transition: all 1s;\r\n}\r\n\r\n.e_calendar_svg_btn-enter {\r\n opacity: 1;\r\n}\r\n\r\n.e_calendar_day-enter-active {\r\n transition: all 0.2s;\r\n}\r\n\r\n.e_calendar_svg_btn-leave-active,\r\n.e_calendar_day-enter {\r\n opacity: 0;\r\n}\r\n\r\n.e_calendar_day-enter {\r\n width: 0;\r\n height: 0;\r\n transform: translate3d(12px, 12px, 0);\r\n}\r\n</style>","<template>\r\n <div ref=\"splitPane\" class=\"split-pane\" :class=\"direction\" :style=\"{ flexDirection: direction }\">\r\n <div class=\"pane pane-one\" :style=\"`${lengthType}: ${paneLengthValue}`\">\r\n <slot name=\"one\"></slot>\r\n </div>\r\n <div\r\n ref=\"trigger\"\r\n class=\"pane-trigger\"\r\n v-if=\"triggerDefaultColor && triggerMoveColor\"\r\n :style=\"`${lengthType}: ${triggerLengthValue}; ${triggerBackGroud}`\"\r\n >\r\n <div\r\n @click=\"handleMouseOver\"\r\n @mouseover=\"handleMouseOver\"\r\n @mouseleave=\"handleMouseLeave\"\r\n @mousedown=\"handleMouseDown\"\r\n class=\"icon\"\r\n :style=\"`${lengthType}: ${triggerLengthValue}`\"\r\n ></div>\r\n </div>\r\n <div class=\"pane pane-two\" :style=\"`${lengthType}: calc(${100 - paneLengthPercent}% - ${triggerLength / 2}px)`\">\r\n <slot name=\"two\"></slot>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, computed, onMounted, nextTick } from 'vue';\r\n\r\nexport default defineComponent({\r\n props: {\r\n direction: {\r\n type: String as () => 'row' | 'column' ,\r\n default: 'row'\r\n },\r\n min: {\r\n type: Number,\r\n required: true\r\n },\r\n max: {\r\n type: Number,\r\n required: true\r\n },\r\n paneLengthPercent: {\r\n type: Number,\r\n required: true\r\n },\r\n triggerLength: {\r\n type: Number,\r\n required: true\r\n }\r\n },\r\n emits: ['update:paneLengthPercent'],\r\n setup(props, { emit }) {\r\n let splitPane = ref<HTMLElement | null>(null);\r\n let trigger = ref<HTMLElement | null>(null);\r\n let triggerLeftOffset = ref(0); // 鼠标距滑动器左(顶)侧偏移量\r\n let triggerBackGroud = ref('');\r\n\r\n // 计算属性\r\n const lengthType = computed(() => (props.direction === 'row' ? 'width' : 'height'));\r\n\r\n // 拖拽条默认颜色\r\n const triggerDefaultColor = computed(() =>\r\n props.direction === 'row'\r\n ? '-webkit-gradient(linear,left top,right top,from(white), to(#D7D7D9))'\r\n : '-webkit-gradient(linear, 0 0, 0 bottom, from(white), to(#D7D7D9))'\r\n );\r\n\r\n // 鼠标拖动时拖拽条的颜色\r\n const triggerMoveColor = computed(() =>\r\n props.direction === 'row'\r\n ? '-webkit-gradient(linear,left top,right top,from(#A6D4E1), to(#2591C8))'\r\n : '-webkit-gradient(linear, 0 0, 0 bottom, from(#A6D4E1), to(#2591C8))'\r\n );\r\n\r\n const paneLengthValue = computed(() => `calc(${props.paneLengthPercent}% - ${props.triggerLength / 2}px)`);\r\n const triggerLengthValue = computed(() => `${props.triggerLength}px`);\r\n\r\n // 生命周期钩子\r\n onMounted(async () => {\r\n await nextTick();\r\n triggerBackGroud.value = `background: ${triggerDefaultColor.value}`;\r\n });\r\n\r\n // 方法\r\n const handleMouseLeave = async () => {\r\n await nextTick();\r\n triggerBackGroud.value = `background: ${triggerDefaultColor.value}`;\r\n };\r\n\r\n const handleMouseOver = async () => {\r\n await nextTick();\r\n triggerBackGroud.value = `background: ${triggerMoveColor.value}`;\r\n };\r\n\r\n // 按下滑动器\r\n const handleMouseDown = (e: MouseEvent) => {\r\n document.addEventListener('mousemove', handleMouseMove);\r\n document.addEventListener('mouseup', handleMouseUp);\r\n\r\n if (props.direction === 'row') {\r\n triggerLeftOffset.value = e.pageX - (e.target as HTMLElement).getBoundingClientRect().left;\r\n } else {\r\n triggerLeftOffset.value = e.pageY - (e.target as HTMLElement).getBoundingClientRect().top;\r\n }\r\n };\r\n\r\n // 按下滑动器后移动鼠标\r\n const handleMouseMove = async (e: MouseEvent) => {\r\n await nextTick();\r\n triggerBackGroud.value = `background: ${triggerMoveColor.value}`;\r\n if (splitPane.value) {\r\n const clientRect = splitPane.value.getBoundingClientRect();\r\n let paneLengthPercent = 0;\r\n if (props.direction === 'row') {\r\n const offset = e.pageX - clientRect.left - triggerLeftOffset.value + props.triggerLength / 2;\r\n paneLengthPercent = (offset / clientRect.width) * 100;\r\n } else {\r\n const offset = e.pageY - clientRect.top - triggerLeftOffset.value + props.triggerLength / 2;\r\n paneLengthPercent = (offset / clientRect.height) * 100;\r\n }\r\n if (paneLengthPercent < props.min) {\r\n paneLengthPercent = props.min;\r\n }\r\n if (paneLengthPercent > props.max) {\r\n paneLengthPercent = props.max;\r\n }\r\n emit('update:paneLengthPercent', paneLengthPercent);\r\n }\r\n };\r\n\r\n // 松开滑动器\r\n const handleMouseUp = async () => {\r\n await nextTick();\r\n triggerBackGroud.value = `background: ${triggerDefaultColor.value}`;\r\n document.removeEventListener('mousemove', handleMouseMove);\r\n };\r\n\r\n return {\r\n splitPane,\r\n trigger,\r\n lengthType,\r\n triggerDefaultColor,\r\n triggerMoveColor,\r\n paneLengthValue,\r\n triggerLengthValue,\r\n triggerBackGroud,\r\n handleMouseLeave,\r\n handleMouseOver,\r\n handleMouseDown,\r\n handleMouseUp\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n$icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABHNCSVQICAgIfAhkiAAAAIJJREFUKFNjZMANBIBS64E4EIg/YFPGiEdzA1AuHogbgRjExgAgzQeA+D8WOQckMZAanJrxOAC3FD5nEzSQYs0gv2HzM1E2k6URZDLI2cihStA2ZAUU+5kk29BtxpVICBoKS2HYFNojCR7ElcJw2dAAlCCYtnFpBuWqDUAcAMRYcxUAYvURjNoDrtgAAAAASUVORK5CYII=';\r\n$background-size: 10px 10px;\r\n\r\n.split-pane {\r\n background: transparent;\r\n height: 100%;\r\n display: flex;\r\n\r\n .pane-one {\r\n background: transparent;\r\n }\r\n\r\n .pane-trigger {\r\n border-radius: 25px;\r\n user-select: none;\r\n border-width: 0.1em;\r\n }\r\n\r\n .pane-two {\r\n flex: 1;\r\n background: transparent;\r\n }\r\n\r\n // 横向布局\r\n &.row {\r\n .pane {\r\n height: 100%;\r\n }\r\n\r\n .pane-trigger {\r\n margin-left: 1px;\r\n margin-right: 1px;\r\n height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n\r\n .icon {\r\n cursor: col-resize;\r\n height: 10px;\r\n width: 100%;\r\n background-image: url(#{$icon});\r\n background-repeat: no-repeat;\r\n background-size: $background-size;\r\n transform: rotate(90deg);\r\n }\r\n }\r\n }\r\n\r\n // 纵向布局\r\n &.column {\r\n .pane {\r\n width: 100%;\r\n }\r\n\r\n .pane-trigger {\r\n margin-top: 1px;\r\n margin-bottom: 1px;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: row;\r\n align-items: center;\r\n justify-content: center;\r\n\r\n .icon {\r\n cursor: row-resize;\r\n height: 100%;\r\n width: 10px;\r\n background-image: url(#{$icon});\r\n background-repeat: no-repeat;\r\n background-size: $background-size;\r\n }\r\n }\r\n }\r\n}\r\n</style>","<template>\r\n <div ref=\"splitPane\" class=\"split-pane\" :class=\"direction\" :style=\"{ flexDirection: direction }\">\r\n <div class=\"pane pane-one\" :style=\"`${lengthType}: ${paneLengthValue}`\">\r\n <slot name=\"one\"></slot>\r\n </div>\r\n <div\r\n ref=\"trigger\"\r\n class=\"pane-trigger\"\r\n v-if=\"triggerDefaultColor && triggerMoveColor\"\r\n :style=\"`${lengthType}: ${triggerLengthValue}; ${triggerBackGroud}`\"\r\n >\r\n <div\r\n @click=\"handleMouseOver\"\r\n @mouseover=\"handleMouseOver\"\r\n @mouseleave=\"handleMouseLeave\"\r\n @mousedown=\"handleMouseDown\"\r\n class=\"icon\"\r\n :style=\"`${lengthType}: ${triggerLengthValue}`\"\r\n ></div>\r\n </div>\r\n <div class=\"pane pane-two\" :style=\"`${lengthType}: calc(${100 - paneLengthPercent}% - ${triggerLength / 2}px)`\">\r\n <slot name=\"two\"></slot>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, computed, onMounted, nextTick } from 'vue';\r\n\r\nexport default defineComponent({\r\n props: {\r\n direction: {\r\n type: String as () => 'row' | 'column' ,\r\n default: 'row'\r\n },\r\n min: {\r\n type: Number,\r\n required: true\r\n },\r\n max: {\r\n type: Number,\r\n required: true\r\n },\r\n paneLengthPercent: {\r\n type: Number,\r\n required: true\r\n },\r\n triggerLength: {\r\n type: Number,\r\n required: true\r\n }\r\n },\r\n emits: ['update:paneLengthPercent'],\r\n setup(props, { emit }) {\r\n let splitPane = ref<HTMLElement | null>(null);\r\n let trigger = ref<HTMLElement | null>(null);\r\n let triggerLeftOffset = ref(0); // 鼠标距滑动器左(顶)侧偏移量\r\n let triggerBackGroud = ref('');\r\n\r\n // 计算属性\r\n const lengthType = computed(() => (props.direction === 'row' ? 'width' : 'height'));\r\n\r\n // 拖拽条默认颜色\r\n const triggerDefaultColor = computed(() =>\r\n props.direction === 'row'\r\n ? '-webkit-gradient(linear,left top,right top,from(white), to(#D7D7D9))'\r\n : '-webkit-gradient(linear, 0 0, 0 bottom, from(white), to(#D7D7D9))'\r\n );\r\n\r\n // 鼠标拖动时拖拽条的颜色\r\n const triggerMoveColor = computed(() =>\r\n props.direction === 'row'\r\n ? '-webkit-gradient(linear,left top,right top,from(#A6D4E1), to(#2591C8))'\r\n : '-webkit-gradient(linear, 0 0, 0 bottom, from(#A6D4E1), to(#2591C8))'\r\n );\r\n\r\n const paneLengthValue = computed(() => `calc(${props.paneLengthPercent}% - ${props.triggerLength / 2}px)`);\r\n const triggerLengthValue = computed(() => `${props.triggerLength}px`);\r\n\r\n // 生命周期钩子\r\n onMounted(async () => {\r\n await nextTick();\r\n triggerBackGroud.value = `background: ${triggerDefaultColor.value}`;\r\n });\r\n\r\n // 方法\r\n const handleMouseLeave = async () => {\r\n await nextTick();\r\n triggerBackGroud.value = `background: ${triggerDefaultColor.value}`;\r\n };\r\n\r\n const handleMouseOver = async () => {\r\n await nextTick();\r\n triggerBackGroud.value = `background: ${triggerMoveColor.value}`;\r\n };\r\n\r\n // 按下滑动器\r\n const handleMouseDown = (e: MouseEvent) => {\r\n document.addEventListener('mousemove', handleMouseMove);\r\n document.addEventListener('mouseup', handleMouseUp);\r\n\r\n if (props.direction === 'row') {\r\n triggerLeftOffset.value = e.pageX - (e.target as HTMLElement).getBoundingClientRect().left;\r\n } else {\r\n triggerLeftOffset.value = e.pageY - (e.target as HTMLElement).getBoundingClientRect().top;\r\n }\r\n };\r\n\r\n // 按下滑动器后移动鼠标\r\n const handleMouseMove = async (e: MouseEvent) => {\r\n await nextTick();\r\n triggerBackGroud.value = `background: ${triggerMoveColor.value}`;\r\n if (splitPane.value) {\r\n const clientRect = splitPane.value.getBoundingClientRect();\r\n let paneLengthPercent = 0;\r\n if (props.direction === 'row') {\r\n const offset = e.pageX - clientRect.left - triggerLeftOffset.value + props.triggerLength / 2;\r\n paneLengthPercent = (offset / clientRect.width) * 100;\r\n } else {\r\n const offset = e.pageY - clientRect.top - triggerLeftOffset.value + props.triggerLength / 2;\r\n paneLengthPercent = (offset / clientRect.height) * 100;\r\n }\r\n if (paneLengthPercent < props.min) {\r\n paneLengthPercent = props.min;\r\n }\r\n if (paneLengthPercent > props.max) {\r\n paneLengthPercent = props.max;\r\n }\r\n emit('update:paneLengthPercent', paneLengthPercent);\r\n }\r\n };\r\n\r\n // 松开滑动器\r\n const handleMouseUp = async () => {\r\n await nextTick();\r\n triggerBackGroud.value = `background: ${triggerDefaultColor.value}`;\r\n document.removeEventListener('mousemove', handleMouseMove);\r\n };\r\n\r\n return {\r\n splitPane,\r\n trigger,\r\n lengthType,\r\n triggerDefaultColor,\r\n triggerMoveColor,\r\n paneLengthValue,\r\n triggerLengthValue,\r\n triggerBackGroud,\r\n handleMouseLeave,\r\n handleMouseOver,\r\n handleMouseDown,\r\n handleMouseUp\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n$icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABHNCSVQICAgIfAhkiAAAAIJJREFUKFNjZMANBIBS64E4EIg/YFPGiEdzA1AuHogbgRjExgAgzQeA+D8WOQckMZAanJrxOAC3FD5nEzSQYs0gv2HzM1E2k6URZDLI2cihStA2ZAUU+5kk29BtxpVICBoKS2HYFNojCR7ElcJw2dAAlCCYtnFpBuWqDUAcAMRYcxUAYvURjNoDrtgAAAAASUVORK5CYII=';\r\n$background-size: 10px 10px;\r\n\r\n.split-pane {\r\n background: transparent;\r\n height: 100%;\r\n display: flex;\r\n\r\n .pane-one {\r\n background: transparent;\r\n }\r\n\r\n .pane-trigger {\r\n border-radius: 25px;\r\n user-select: none;\r\n border-width: 0.1em;\r\n }\r\n\r\n .pane-two {\r\n flex: 1;\r\n background: transparent;\r\n }\r\n\r\n // 横向布局\r\n &.row {\r\n .pane {\r\n height: 100%;\r\n }\r\n\r\n .pane-trigger {\r\n margin-left: 1px;\r\n margin-right: 1px;\r\n height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n\r\n .icon {\r\n cursor: col-resize;\r\n height: 10px;\r\n width: 100%;\r\n background-image: url(#{$icon});\r\n background-repeat: no-repeat;\r\n background-size: $background-size;\r\n transform: rotate(90deg);\r\n }\r\n }\r\n }\r\n\r\n // 纵向布局\r\n &.column {\r\n .pane {\r\n width: 100%;\r\n }\r\n\r\n .pane-trigger {\r\n margin-top: 1px;\r\n margin-bottom: 1px;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: row;\r\n align-items: center;\r\n justify-content: center;\r\n\r\n .icon {\r\n cursor: row-resize;\r\n height: 100%;\r\n width: 10px;\r\n background-image: url(#{$icon});\r\n background-repeat: no-repeat;\r\n background-size: $background-size;\r\n }\r\n }\r\n }\r\n}\r\n</style>","<template>\r\n <div class=\"header\">\r\n <template v-for='(item, index) in headers' :key=\"index\">\r\n <div \r\n :property='item.property' \r\n :columnindex='index' \r\n v-show=\"item.show\" \r\n class=\"headerCaption\"\r\n :style=\"{ width: `${item.width}px` }\"\r\n >\r\n <span>{{ getHeaderTitle(item) }}</span>\r\n <!-- 列宽调整拖动手柄 -->\r\n <div \r\n v-if=\"index < headers.length - 1 && item.show\"\r\n class=\"resize-handle\"\r\n @mousedown=\"startResize($event, index)\"\r\n ></div>\r\n </div>\r\n </template>\r\n </div>\r\n </template>\r\n\r\n <script lang=\"ts\">\r\n import { defineComponent, ref } from 'vue';\r\n import { useI18n } from '../i18n';\r\n\r\n export default defineComponent({\r\n props: {\r\n headers: {\r\n type: Array as () => {\r\n property: string;\r\n show: boolean;\r\n width: number;\r\n title: string;\r\n }[],\r\n required: true\r\n }\r\n },\r\n setup(props) {\r\n const { t } = useI18n();\r\n \r\n // 拖动调整状态\r\n const resizing = ref(false);\r\n const resizingIndex = ref(-1);\r\n const startX = ref(0);\r\n const startWidth = ref(0);\r\n const currentHeaderElement = ref<HTMLElement | null>(null);\r\n \r\n // 根据 property 获取翻译后的标题\r\n const getHeaderTitle = (header: { property: string; title: string }) => {\r\n const propertyMap: Record<string, string> = {\r\n 'no': 'task.serialNumber',\r\n 'task': 'task.name',\r\n 'priority': 'task.priority',\r\n 'startdate': 'task.startDate',\r\n 'enddate': 'task.endDate',\r\n 'takestime': 'task.duration',\r\n 'progress': 'task.progress',\r\n 'id': 'ID',\r\n 'parentId': 'Parent ID'\r\n };\r\n \r\n const translationKey = propertyMap[header.property];\r\n if (translationKey) {\r\n if (translationKey.includes('.')) {\r\n return t(translationKey);\r\n }\r\n return translationKey;\r\n }\r\n return header.title;\r\n };\r\n \r\n // 开始拖动调整列宽\r\n const startResize = (event: MouseEvent, index: number) => {\r\n event.preventDefault();\r\n event.stopPropagation();\r\n \r\n resizing.value = true;\r\n resizingIndex.value = index;\r\n startX.value = event.clientX;\r\n startWidth.value = props.headers[index].width;\r\n \r\n // 获取当前列的 DOM 元素\r\n const target = event.target as HTMLElement;\r\n currentHeaderElement.value = target.closest('.headerCaption') as HTMLElement;\r\n \r\n // 添加全局鼠标移动和释放事件\r\n document.addEventListener('mousemove', handleMouseMove, { passive: false });\r\n document.addEventListener('mouseup', handleMouseUp);\r\n \r\n // 添加禁止选择的样式\r\n document.body.style.cursor = 'col-resize';\r\n document.body.style.userSelect = 'none';\r\n };\r\n \r\n // 处理鼠标移动 - 使用 requestAnimationFrame 优化性能\r\n let rafId: number | null = null;\r\n const handleMouseMove = (event: MouseEvent) => {\r\n if (!resizing.value || resizingIndex.value < 0) return;\r\n \r\n event.preventDefault();\r\n \r\n // 取消之前的动画帧\r\n if (rafId !== null) {\r\n cancelAnimationFrame(rafId);\r\n }\r\n \r\n // 使用 requestAnimationFrame 确保流畅更新\r\n rafId = requestAnimationFrame(() => {\r\n const deltaX = event.clientX - startX.value;\r\n const newWidth = Math.max(50, startWidth.value + deltaX);\r\n \r\n // 直接设置 DOM 样式,避免 Vue 响应式更新延迟\r\n if (currentHeaderElement.value) {\r\n currentHeaderElement.value.style.width = `${newWidth}px`;\r\n }\r\n \r\n // 同时更新对应的内容列\r\n const contentCells = document.querySelectorAll(\r\n `.cellNo[columnindex=\"${resizingIndex.value}\"], .cell[columnindex=\"${resizingIndex.value}\"]`\r\n );\r\n contentCells.forEach((cell) => {\r\n (cell as HTMLElement).style.minWidth = `${newWidth}px`;\r\n (cell as HTMLElement).style.maxWidth = `${newWidth}px`;\r\n });\r\n \r\n rafId = null;\r\n });\r\n };\r\n \r\n // 处理鼠标释放\r\n const handleMouseUp = () => {\r\n if (!resizing.value) return;\r\n \r\n // 取消未完成的动画帧\r\n if (rafId !== null) {\r\n cancelAnimationFrame(rafId);\r\n rafId = null;\r\n }\r\n \r\n // 计算最终宽度并更新到 props\r\n if (currentHeaderElement.value && resizingIndex.value >= 0) {\r\n const finalWidth = parseInt(currentHeaderElement.value.style.width);\r\n props.headers[resizingIndex.value].width = finalWidth;\r\n }\r\n \r\n resizing.value = false;\r\n resizingIndex.value = -1;\r\n currentHeaderElement.value = null;\r\n \r\n // 移除全局事件监听\r\n document.removeEventListener('mousemove', handleMouseMove);\r\n document.removeEventListener('mouseup', handleMouseUp);\r\n \r\n // 恢复样式\r\n document.body.style.cursor = '';\r\n document.body.style.userSelect = '';\r\n };\r\n \r\n return {\r\n getHeaderTitle,\r\n startResize\r\n };\r\n }\r\n });\r\n </script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.header {\r\n height: 100%;\r\n display: flex;\r\n flex-flow: row nowrap;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n font-family: var(--font-family, 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif);\r\n \r\n .headerCaption {\r\n text-align: center;\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: center;\r\n position: relative;\r\n color: var(--text-primary, #333333);\r\n font-size: 14px;\r\n font-weight: 600;\r\n box-sizing: border-box;\r\n letter-spacing: 0.5px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n transition: all var(--transition-fast, 0.15s ease);\r\n flex-shrink: 0;\r\n \r\n &::before {\r\n content: '';\r\n position: absolute;\r\n top: 0;\r\n right: 0;\r\n bottom: 0;\r\n left: 0;\r\n border-top: 1px solid var(--border, #cecece);\r\n border-bottom: 1px solid var(--border, #cecece);\r\n pointer-events: none;\r\n }\r\n \r\n &:not(:last-child)::after {\r\n content: '';\r\n position: absolute;\r\n right: -1px;\r\n top: 0;\r\n bottom: 0;\r\n background: var(--border, #cecece);\r\n border-left: 1px solid var(--border, #cecece);\r\n }\r\n \r\n &:first-child::before {\r\n border-left: 1px solid var(--border, #cecece);\r\n }\r\n\r\n &:last-child::before {\r\n border-right: 1px solid var(--border, #cecece);\r\n }\r\n\r\n &:hover {\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n color: var(--primary, #0078d4);\r\n }\r\n\r\n // 列宽调整拖动手柄\r\n .resize-handle {\r\n position: absolute;\r\n right: -4px;\r\n top: 0;\r\n bottom: 0;\r\n width: 8px;\r\n cursor: col-resize;\r\n z-index: 10;\r\n background: transparent;\r\n transition: background 0.2s ease;\r\n\r\n &::before {\r\n content: '';\r\n position: absolute;\r\n left: 50%;\r\n top: 50%;\r\n transform: translate(-50%, -50%);\r\n width: 2px;\r\n height: 60%;\r\n background: transparent;\r\n transition: background 0.2s ease;\r\n }\r\n\r\n &:hover {\r\n background: rgba(51, 112, 255, 0.1);\r\n\r\n &::before {\r\n background: var(--primary, #3370ff);\r\n }\r\n }\r\n\r\n &:active {\r\n background: rgba(51, 112, 255, 0.15);\r\n }\r\n }\r\n }\r\n}\r\n</style>","<template>\r\n <div class=\"header\">\r\n <template v-for='(item, index) in headers' :key=\"index\">\r\n <div \r\n :property='item.property' \r\n :columnindex='index' \r\n v-show=\"item.show\" \r\n class=\"headerCaption\"\r\n :style=\"{ width: `${item.width}px` }\"\r\n >\r\n <span>{{ getHeaderTitle(item) }}</span>\r\n <!-- 列宽调整拖动手柄 -->\r\n <div \r\n v-if=\"index < headers.length - 1 && item.show\"\r\n class=\"resize-handle\"\r\n @mousedown=\"startResize($event, index)\"\r\n ></div>\r\n </div>\r\n </template>\r\n </div>\r\n </template>\r\n\r\n <script lang=\"ts\">\r\n import { defineComponent, ref } from 'vue';\r\n import { useI18n } from '../i18n';\r\n\r\n export default defineComponent({\r\n props: {\r\n headers: {\r\n type: Array as () => {\r\n property: string;\r\n show: boolean;\r\n width: number;\r\n title: string;\r\n }[],\r\n required: true\r\n }\r\n },\r\n setup(props) {\r\n const { t } = useI18n();\r\n \r\n // 拖动调整状态\r\n const resizing = ref(false);\r\n const resizingIndex = ref(-1);\r\n const startX = ref(0);\r\n const startWidth = ref(0);\r\n const currentHeaderElement = ref<HTMLElement | null>(null);\r\n \r\n // 根据 property 获取翻译后的标题\r\n const getHeaderTitle = (header: { property: string; title: string }) => {\r\n const propertyMap: Record<string, string> = {\r\n 'no': 'task.serialNumber',\r\n 'task': 'task.name',\r\n 'priority': 'task.priority',\r\n 'startdate': 'task.startDate',\r\n 'enddate': 'task.endDate',\r\n 'takestime': 'task.duration',\r\n 'progress': 'task.progress',\r\n 'id': 'ID',\r\n 'parentId': 'Parent ID'\r\n };\r\n \r\n const translationKey = propertyMap[header.property];\r\n if (translationKey) {\r\n if (translationKey.includes('.')) {\r\n return t(translationKey);\r\n }\r\n return translationKey;\r\n }\r\n return header.title;\r\n };\r\n \r\n // 开始拖动调整列宽\r\n const startResize = (event: MouseEvent, index: number) => {\r\n event.preventDefault();\r\n event.stopPropagation();\r\n \r\n resizing.value = true;\r\n resizingIndex.value = index;\r\n startX.value = event.clientX;\r\n startWidth.value = props.headers[index].width;\r\n \r\n // 获取当前列的 DOM 元素\r\n const target = event.target as HTMLElement;\r\n currentHeaderElement.value = target.closest('.headerCaption') as HTMLElement;\r\n \r\n // 添加全局鼠标移动和释放事件\r\n document.addEventListener('mousemove', handleMouseMove, { passive: false });\r\n document.addEventListener('mouseup', handleMouseUp);\r\n \r\n // 添加禁止选择的样式\r\n document.body.style.cursor = 'col-resize';\r\n document.body.style.userSelect = 'none';\r\n };\r\n \r\n // 处理鼠标移动 - 使用 requestAnimationFrame 优化性能\r\n let rafId: number | null = null;\r\n const handleMouseMove = (event: MouseEvent) => {\r\n if (!resizing.value || resizingIndex.value < 0) return;\r\n \r\n event.preventDefault();\r\n \r\n // 取消之前的动画帧\r\n if (rafId !== null) {\r\n cancelAnimationFrame(rafId);\r\n }\r\n \r\n // 使用 requestAnimationFrame 确保流畅更新\r\n rafId = requestAnimationFrame(() => {\r\n const deltaX = event.clientX - startX.value;\r\n const newWidth = Math.max(50, startWidth.value + deltaX);\r\n \r\n // 直接设置 DOM 样式,避免 Vue 响应式更新延迟\r\n if (currentHeaderElement.value) {\r\n currentHeaderElement.value.style.width = `${newWidth}px`;\r\n }\r\n \r\n // 同时更新对应的内容列\r\n const contentCells = document.querySelectorAll(\r\n `.cellNo[columnindex=\"${resizingIndex.value}\"], .cell[columnindex=\"${resizingIndex.value}\"]`\r\n );\r\n contentCells.forEach((cell) => {\r\n (cell as HTMLElement).style.minWidth = `${newWidth}px`;\r\n (cell as HTMLElement).style.maxWidth = `${newWidth}px`;\r\n });\r\n \r\n rafId = null;\r\n });\r\n };\r\n \r\n // 处理鼠标释放\r\n const handleMouseUp = () => {\r\n if (!resizing.value) return;\r\n \r\n // 取消未完成的动画帧\r\n if (rafId !== null) {\r\n cancelAnimationFrame(rafId);\r\n rafId = null;\r\n }\r\n \r\n // 计算最终宽度并更新到 props\r\n if (currentHeaderElement.value && resizingIndex.value >= 0) {\r\n const finalWidth = parseInt(currentHeaderElement.value.style.width);\r\n props.headers[resizingIndex.value].width = finalWidth;\r\n }\r\n \r\n resizing.value = false;\r\n resizingIndex.value = -1;\r\n currentHeaderElement.value = null;\r\n \r\n // 移除全局事件监听\r\n document.removeEventListener('mousemove', handleMouseMove);\r\n document.removeEventListener('mouseup', handleMouseUp);\r\n \r\n // 恢复样式\r\n document.body.style.cursor = '';\r\n document.body.style.userSelect = '';\r\n };\r\n \r\n return {\r\n getHeaderTitle,\r\n startResize\r\n };\r\n }\r\n });\r\n </script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.header {\r\n height: 100%;\r\n display: flex;\r\n flex-flow: row nowrap;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n font-family: var(--font-family, 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif);\r\n \r\n .headerCaption {\r\n text-align: center;\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: center;\r\n position: relative;\r\n color: var(--text-primary, #333333);\r\n font-size: 14px;\r\n font-weight: 600;\r\n box-sizing: border-box;\r\n letter-spacing: 0.5px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n transition: all var(--transition-fast, 0.15s ease);\r\n flex-shrink: 0;\r\n \r\n &::before {\r\n content: '';\r\n position: absolute;\r\n top: 0;\r\n right: 0;\r\n bottom: 0;\r\n left: 0;\r\n border-top: 1px solid var(--border, #cecece);\r\n border-bottom: 1px solid var(--border, #cecece);\r\n pointer-events: none;\r\n }\r\n \r\n &:not(:last-child)::after {\r\n content: '';\r\n position: absolute;\r\n right: -1px;\r\n top: 0;\r\n bottom: 0;\r\n background: var(--border, #cecece);\r\n border-left: 1px solid var(--border, #cecece);\r\n }\r\n \r\n &:first-child::before {\r\n border-left: 1px solid var(--border, #cecece);\r\n }\r\n\r\n &:last-child::before {\r\n border-right: 1px solid var(--border, #cecece);\r\n }\r\n\r\n &:hover {\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n color: var(--primary, #0078d4);\r\n }\r\n\r\n // 列宽调整拖动手柄\r\n .resize-handle {\r\n position: absolute;\r\n right: -4px;\r\n top: 0;\r\n bottom: 0;\r\n width: 8px;\r\n cursor: col-resize;\r\n z-index: 10;\r\n background: transparent;\r\n transition: background 0.2s ease;\r\n\r\n &::before {\r\n content: '';\r\n position: absolute;\r\n left: 50%;\r\n top: 50%;\r\n transform: translate(-50%, -50%);\r\n width: 2px;\r\n height: 60%;\r\n background: transparent;\r\n transition: background 0.2s ease;\r\n }\r\n\r\n &:hover {\r\n background: rgba(51, 112, 255, 0.1);\r\n\r\n &::before {\r\n background: var(--primary, #3370ff);\r\n }\r\n }\r\n\r\n &:active {\r\n background: rgba(51, 112, 255, 0.15);\r\n }\r\n }\r\n }\r\n}\r\n</style>","import { reactive } from 'vue';\r\n\r\n// 定义 Store 接口\r\ninterface StoreType {\r\n monthHeaders: any[];\r\n weekHeaders: any[];\r\n dayHeaders: any[];\r\n hourHeaders: any[];\r\n tasks: any[];\r\n taskHeaders: any[];\r\n mapFields: Record<string, any>;\r\n scale: number;\r\n timelineCellCount: number;\r\n startGanttDate: Date | null;\r\n endGanttDate: Date | null;\r\n scrollFlag: boolean;\r\n mode: string | null;\r\n expandRow: {\r\n pid: number;\r\n expand: boolean;\r\n };\r\n collapsedTasks: Set<any>; // 记录已折叠的任务ID集合\r\n rootTask: any;\r\n subTask: any;\r\n editTask: any;\r\n removeTask: any;\r\n allowChangeTaskDate: any;\r\n barDate: {\r\n id: string;\r\n startDate: string;\r\n endDate: string;\r\n };\r\n}\r\n\r\n// 初始状态\r\nconst initialStore: StoreType = {\r\n monthHeaders: [],\r\n weekHeaders: [],\r\n dayHeaders: [],\r\n hourHeaders: [],\r\n tasks: [],\r\n taskHeaders: [],\r\n mapFields: {},\r\n scale: 90,\r\n timelineCellCount: 0,\r\n startGanttDate: null,\r\n endGanttDate: null,\r\n scrollFlag: true,\r\n mode: null,\r\n expandRow: {\r\n pid: 0,\r\n expand: true\r\n },\r\n collapsedTasks: new Set(),\r\n rootTask: {},\r\n subTask: {},\r\n editTask: {},\r\n removeTask: {},\r\n allowChangeTaskDate: {},\r\n barDate: {\r\n id: '',\r\n startDate: '',\r\n endDate: ''\r\n }\r\n};\r\n\r\nexport let serialNumber: number = 0;\r\n// 使用reactive保证响应式正常工作\r\nexport let store = reactive(initialStore) as StoreType;\r\n\r\n// 定义 Mutations 类型\r\ninterface MutationsType {\r\n setMonthHeaders: (monthHeaders: any[]) => void;\r\n setDayHeaders: (dayHeaders: any[]) => void;\r\n setTasks: (tasks: any[]) => void;\r\n setTaskHeaders: (taskHeaders: any[]) => void;\r\n setWeekHeaders: (weekHeaders: any[]) => void;\r\n setHourHeaders: (hourHeaders: any[]) => void;\r\n setScale: (scale: number) => void;\r\n setMapFields: (mapFields: Record<string, any>) => void;\r\n setTimelineCellCount: (timelineCellCount: number) => void;\r\n setStartGanttDate: (startGanttDate: Date | null) => void;\r\n setEndGanttDate: (endGanttDate: Date | null) => void;\r\n setScrollFlag: (scrollFlag: boolean) => void;\r\n setMode: (mode: string | null) => void;\r\n setExpandRow: (expandRow: { pid: number; expand: boolean }) => void;\r\n toggleTaskCollapse: (taskId: any) => void; // 切换任务折叠状态\r\n setRootTask: (rootTask: any) => void;\r\n setSubTask: (subTask: any) => void;\r\n setEditTask: (editTask: any) => void;\r\n setRemoveTask: (removeTask: any) => void;\r\n setBarDate: (barDate: { id: string; startDate: string; endDate: string }) => void;\r\n setAllowChangeTaskDate: (task: any) => void;\r\n}\r\n\r\n// 定义 Mutations\r\nexport let mutations: MutationsType = {\r\n setMonthHeaders(monthHeaders: any[]): void {\r\n store.monthHeaders = monthHeaders;\r\n },\r\n setDayHeaders(dayHeaders: any[]): void {\r\n store.dayHeaders = dayHeaders;\r\n },\r\n setTasks(tasks: any[]): void {\r\n store.tasks = tasks;\r\n },\r\n setTaskHeaders(taskHeaders: any[]): void {\r\n store.taskHeaders = taskHeaders;\r\n },\r\n setWeekHeaders(weekHeaders: any[]): void {\r\n store.weekHeaders = weekHeaders;\r\n },\r\n setHourHeaders(hourHeaders: any[]): void {\r\n store.hourHeaders = hourHeaders;\r\n },\r\n setScale(scale: number): void {\r\n store.scale = scale;\r\n },\r\n setMapFields(mapFields: Record<string, any>): void {\r\n store.mapFields = mapFields;\r\n },\r\n setTimelineCellCount(timelineCellCount: number): void {\r\n store.timelineCellCount = timelineCellCount;\r\n },\r\n setStartGanttDate(startGanttDate: Date | null): void {\r\n store.startGanttDate = startGanttDate;\r\n },\r\n setEndGanttDate(endGanttDate: Date | null): void {\r\n store.endGanttDate = endGanttDate;\r\n },\r\n setScrollFlag(scrollFlag: boolean): void {\r\n store.scrollFlag = scrollFlag;\r\n },\r\n setMode(mode: string | null): void {\r\n store.mode = mode;\r\n },\r\n setExpandRow(expandRow: { pid: number; expand: boolean }): void {\r\n store.expandRow = expandRow;\r\n },\r\n toggleTaskCollapse(taskId: any): void {\r\n if (store.collapsedTasks.has(taskId)) {\r\n store.collapsedTasks.delete(taskId);\r\n } else {\r\n store.collapsedTasks.add(taskId);\r\n }\r\n // 触发响应式更新\r\n store.collapsedTasks = new Set(store.collapsedTasks);\r\n },\r\n setRootTask(rootTask: any): void {\r\n store.rootTask = rootTask;\r\n },\r\n setSubTask(subTask: any): void {\r\n store.subTask = subTask;\r\n },\r\n setEditTask(editTask: any): void {\r\n store.editTask = editTask;\r\n },\r\n setRemoveTask(removeTask: any): void {\r\n store.removeTask = removeTask;\r\n },\r\n setBarDate(barDate: { id: string; startDate: string; endDate: string }): void {\r\n store.barDate = barDate;\r\n },\r\n setAllowChangeTaskDate(task: any): void {\r\n store.allowChangeTaskDate = task;\r\n }\r\n};","import { reactive, ref } from 'vue';\r\n\r\nconst sharedState = reactive({\r\n shouldScrollToToday: false,\r\n triggerScrollToToday() {\r\n this.shouldScrollToToday = true;\r\n },\r\n\r\n shouldScroll: false,\r\n triggerScroll() {\r\n this.shouldScroll = true;\r\n },\r\n\r\n highlightedId: <number | null> null,\r\n triggerHighlight(id: number | null) {\r\n this.highlightedId = id;\r\n }\r\n});\r\nexport default sharedState;\r\n\r\n// 创建一个 ref 来存储滚动位置 \r\nconst scrollTop = ref(0); \r\n// 创建一个 ref 来存储滚动标志 \r\nconst scrollFlag = ref(false); \r\n \r\n// 定义设置滚动位置的函数 \r\n// 为了避免隐式的 'any' 类型错误,明确指定参数 'value' 的类型为 'number'\r\nconst setScrollTop = (value: number) => {\r\n scrollTop.value = value; \r\n}; \r\n \r\n// 定义设置滚动标志的函数 \r\n// 为了避免隐式的 'any' 类型错误,明确指定参数 'value' 的类型为 'boolean'\r\nconst setScrollFlag = (value: boolean) => {\r\n scrollFlag.value = value; \r\n}; \r\n \r\n// 导出共享状态和方法 \r\nexport const useScrollState = () => { \r\n return { \r\n scrollTop, \r\n scrollFlag, \r\n setScrollTop, \r\n setScrollFlag \r\n }; \r\n}; ","<template>\r\n <div v-if='showRow' @mouseover=\"hoverActive()\" @mouseleave=\"hoverInactive()\" :class=\"{ active: hover }\">\r\n <div class=\"row\" @dblclick=\"setEditTask(row)\" v-bind:style=\"{ height: rowHeight + 'px' }\">\r\n <template v-for='(header, headerIndex) in headers'>\r\n <div \r\n class=\"cellNo\" \r\n :key=\"headerIndex\" \r\n :columnindex=\"headerIndex\"\r\n v-if=\"header.property === 'no'\" \r\n v-bind:style=\"{\r\n minWidth: header.width + 'px',\r\n maxWidth: header.width + 'px',\r\n height: rowHeight + 'px'\r\n }\">\r\n <div class=\"no-cell-content\">\r\n <!-- 树形连线 -->\r\n <div class=\"tree-lines\">\r\n <!-- 祖先节点的贯穿线(连接兄弟节点) -->\r\n <div \r\n v-for=\"level in getAncestorLines\" \r\n :key=\"'ancestor-' + level\" \r\n class=\"tree-line-vertical ancestor\"\r\n :style=\"{ left: level * 16 + 8 + 'px' }\"\r\n ></div>\r\n \r\n <!-- 顶级节点有子节点且未折叠时,显示向下的竖线 -->\r\n <div \r\n v-if=\"row.treeLevel === 1 && hasChildren && !isCollapsed\"\r\n class=\"tree-line-vertical parent-to-child\"\r\n :style=\"{ left: row.treeLevel * 16 + 8 + 'px' }\"\r\n ></div>\r\n \r\n <!-- 子节点的连接线 -->\r\n <template v-if=\"row.treeLevel && row.treeLevel > 1\">\r\n <!-- 当前节点的垂直线 -->\r\n <div \r\n class=\"tree-line-vertical current\"\r\n :class=\"{ 'is-last-child': isLastChild }\"\r\n :style=\"{ left: (row.treeLevel - 1) * 16 + 8 + 'px' }\"\r\n ></div>\r\n \r\n <!-- 水平线(连接到当前节点) -->\r\n <div \r\n class=\"tree-line-horizontal\"\r\n :style=\"{ left: (row.treeLevel - 1) * 16 + 8 + 'px', width: '32px' }\"\r\n ></div>\r\n \r\n <!-- 如果当前节点有子节点且未折叠,显示向下的竖线 -->\r\n <div \r\n v-if=\"hasChildren && !isCollapsed\"\r\n class=\"tree-line-vertical parent-to-child\"\r\n :style=\"{ left: row.treeLevel * 16 + 8 + 'px' }\"\r\n ></div>\r\n </template>\r\n </div>\r\n \r\n <!-- 左侧:折叠按钮 + 序号 -->\r\n <div class=\"no-left-section\" :style=\"{ paddingLeft: (row.treeLevel || 0) * 16 + 'px' }\">\r\n <!-- 折叠/展开按钮 -->\r\n <button \r\n v-if=\"hasChildren\" \r\n @click.stop=\"toggleCollapse\" \r\n class=\"collapse-btn\"\r\n :class=\"{ collapsed: isCollapsed }\"\r\n :title=\"isCollapsed ? '展开' : '折叠'\"\r\n >\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\">\r\n <!-- 三角形箭头 -->\r\n <path \r\n class=\"arrow\" \r\n d=\"M5 4 L14 9 L5 14 Z\" \r\n fill=\"currentColor\"\r\n />\r\n </svg>\r\n </button>\r\n <!-- 无子节点时的占位空间(透明,不遮挡横线) -->\r\n <span v-else class=\"collapse-placeholder\"></span>\r\n \r\n <!-- 序号 -->\r\n <span class=\"no-text\">{{ row.no }}</span>\r\n </div>\r\n \r\n <!-- 右侧:操作按钮(鼠标悬停显示) -->\r\n <div class=\"action-buttons\">\r\n <button \r\n @click.stop=\"setSubTask(row)\" \r\n class=\"action-btn add-btn\"\r\n :title=\"'添加子任务'\"\r\n >\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"currentColor\">\r\n <path d=\"M8 2a.5.5 0 01.5.5v5h5a.5.5 0 010 1h-5v5a.5.5 0 01-1 0v-5h-5a.5.5 0 010-1h5v-5A.5.5 0 018 2z\"/>\r\n </svg>\r\n </button>\r\n <button \r\n @click.stop=\"setRemoveTask(row)\" \r\n class=\"action-btn delete-btn\"\r\n :title=\"'删除任务'\"\r\n >\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"currentColor\">\r\n <path d=\"M5.5 5.5A.5.5 0 016 6v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm2.5 0a.5.5 0 01.5.5v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm3 .5a.5.5 0 00-1 0v6a.5.5 0 001 0V6z\"/>\r\n <path fill-rule=\"evenodd\" d=\"M14.5 3a1 1 0 01-1 1H13v9a2 2 0 01-2 2H5a2 2 0 01-2-2V4h-.5a1 1 0 01-1-1V2a1 1 0 011-1H6a1 1 0 011-1h2a1 1 0 011 1h3.5a1 1 0 011 1v1zM4.118 4L4 4.059V13a1 1 0 001 1h6a1 1 0 001-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z\"/>\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n <div \r\n v-else \r\n v-show=\"header.show\" \r\n class=\"cell\" \r\n :key=\"headerIndex + '-header'\"\r\n :columnindex=\"headerIndex\"\r\n :style=\"{ minWidth: header.width + 'px', maxWidth: header.width + 'px', height: rowHeight + 'px' }\">\r\n {{ checkField(row, header.property) }}\r\n </div>\r\n </template>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, onMounted, computed, inject, watch } from 'vue';\r\nimport { store, mutations } from '../Store';\r\nimport sharedState from '../ShareState';\r\n\r\nexport default defineComponent({\r\n props: {\r\n headers: {\r\n type: Array as () => any[],\r\n default: () => []\r\n },\r\n rowHeight: {\r\n type: Number,\r\n default: 0\r\n },\r\n row: {\r\n type: Object as () => Record<string, any>,\r\n default: () => ({})\r\n }\r\n },\r\n setup(props) {\r\n const showRow = ref(true);\r\n const hover = ref(false);\r\n const addTips = '添加子任务';\r\n const removeTips = '删除当前任务';\r\n\r\n const mapFields = computed(() => store.mapFields);\r\n const subTask = computed(() => store.subTask);\r\n const collapsedTasks = computed(() => store.collapsedTasks);\r\n\r\n const barHover = inject('barHover') as ((rowId: any, hover: boolean) => void) | undefined;\r\n const addRootTask = inject('addRootTask') as ((row: any) => void) | undefined;\r\n\r\n // 判断当前任务是否有子任务\r\n const hasChildren = computed(() => {\r\n const currentId = props.row[mapFields.value['id']];\r\n return store.tasks.some(task => task[mapFields.value['parentId']] === currentId);\r\n });\r\n\r\n // 判断当前任务是否已折叠\r\n const isCollapsed = computed(() => {\r\n const currentId = props.row[mapFields.value['id']];\r\n return collapsedTasks.value.has(currentId);\r\n });\r\n \r\n // 判断是否是最后一个子节点\r\n const isLastChild = computed(() => {\r\n const parentId = props.row[mapFields.value['parentId']];\r\n if (!parentId || parentId === '0') return false;\r\n \r\n const siblings = store.tasks.filter(task => \r\n task[mapFields.value['parentId']] === parentId\r\n );\r\n \r\n if (siblings.length === 0) return false;\r\n \r\n const currentId = props.row[mapFields.value['id']];\r\n const lastSibling = siblings[siblings.length - 1];\r\n return lastSibling[mapFields.value['id']] === currentId;\r\n });\r\n \r\n // 获取需要显示祖先贯穿线的层级\r\n const getAncestorLines = computed(() => {\r\n const lines: number[] = [];\r\n const treeLevel = props.row.treeLevel || 0;\r\n \r\n if (treeLevel <= 1) return lines;\r\n \r\n // 当前节点的 current 线位置对应的层级(需要过滤掉,避免重叠)\r\n const currentLineLevel = treeLevel - 1;\r\n \r\n // 构建从根到当前节点的路径\r\n const path: any[] = [];\r\n let currentTask = props.row;\r\n \r\n while (currentTask) {\r\n path.unshift(currentTask);\r\n const parentId = currentTask[mapFields.value['parentId']];\r\n if (!parentId || parentId === '0') break;\r\n \r\n const parent = store.tasks.find(task => \r\n task[mapFields.value['id']] === parentId\r\n );\r\n if (!parent) break;\r\n currentTask = parent;\r\n }\r\n \r\n // 从直接父节点开始向上检查,遇到\"最后一个子节点\"就停止\r\n for (let i = path.length - 2; i >= 0; i--) {\r\n const node = path[i];\r\n const nodeId = node[mapFields.value['id']];\r\n const parentId = node[mapFields.value['parentId']];\r\n \r\n // 查找该节点的所有兄弟节点\r\n const siblings = store.tasks.filter(task => \r\n task[mapFields.value['parentId']] === parentId\r\n );\r\n \r\n if (siblings.length > 0) {\r\n const lastSibling = siblings[siblings.length - 1];\r\n const isLast = lastSibling[mapFields.value['id']] === nodeId;\r\n \r\n if (isLast) {\r\n // 如果这个节点是最后一个子节点,停止添加贯穿线\r\n break;\r\n } else {\r\n // 如果不是最后一个,需要显示贯穿线\r\n const nodeTreeLevel = node.treeLevel;\r\n // 过滤掉和 current 线位置相同的层级\r\n if (nodeTreeLevel && nodeTreeLevel >= 1 && nodeTreeLevel !== currentLineLevel) {\r\n lines.push(nodeTreeLevel);\r\n }\r\n }\r\n }\r\n }\r\n \r\n return lines;\r\n });\r\n \r\n // 切换折叠状态\r\n const toggleCollapse = () => {\r\n const currentId = props.row[mapFields.value['id']];\r\n mutations.toggleTaskCollapse(currentId);\r\n };\r\n\r\n onMounted(() => {\r\n\r\n });\r\n\r\n const setSubTask = mutations.setSubTask;\r\n const setEditTask = mutations.setEditTask;\r\n const setRemoveTask = mutations.setRemoveTask;\r\n\r\n const checkField = (row: Record<string, any>, property: string) => {\r\n if (mapFields.value[property]) {\r\n return row[mapFields.value[property]];\r\n } else if (row[property]) {\r\n return row[property];\r\n }\r\n return null;\r\n };\r\n\r\n watch(() => sharedState.highlightedId, (newId) => {\r\n if (props.row[mapFields.value['id']] === newId) {\r\n hover.value = true;\r\n } else {\r\n hover.value = false;\r\n }\r\n });\r\n\r\n const hoverActive = () => {\r\n sharedState.triggerHighlight(props.row[mapFields.value.id] as number|null);\r\n };\r\n\r\n const hoverInactive = () => {\r\n sharedState.triggerHighlight(null);\r\n };\r\n\r\n const handleAddRootTask = () => {\r\n if (addRootTask) {\r\n addRootTask(props.row);\r\n }\r\n };\r\n\r\n return {\r\n showRow,\r\n hover,\r\n addTips,\r\n removeTips,\r\n mapFields,\r\n subTask,\r\n hasChildren,\r\n isCollapsed,\r\n isLastChild,\r\n getAncestorLines,\r\n toggleCollapse,\r\n setSubTask,\r\n setEditTask,\r\n setRemoveTask,\r\n checkField,\r\n hoverActive,\r\n hoverInactive,\r\n addRootTask: handleAddRootTask\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.active .row {\r\n background: var(--row-hover, #FFF3A1) !important;\r\n}\r\n\r\n.row {\r\n display: flex;\r\n flex-flow: row nowrap;\r\n align-items: center;\r\n justify-content: flex-start;\r\n border-top: none;\r\n border-bottom: none;\r\n width: fit-content;\r\n background: var(--bg-content, #ffffff);\r\n color: var(--text-primary, #333333);\r\n\r\n &:first-child {\r\n border-top: 1px solid var(--border, #cecece);\r\n border-bottom: none;\r\n }\r\n\r\n &:not(:first-child:last-child) {\r\n border-right: 1px solid var(--border, #cecece);\r\n border-top: 1px solid var(--border, #cecece);\r\n border-bottom: 1px solid var(--border, #cecece);\r\n }\r\n\r\n &:last-child {\r\n border-top: none;\r\n border-bottom: 1px solid var(--border, #cecece);\r\n }\r\n\r\n .cellNo {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n font-size: 12px;\r\n border-top: none;\r\n border-bottom: none;\r\n margin: 0px 0px 0px 1px;\r\n position: relative;\r\n color: var(--text-primary, #333333);\r\n padding: 0 8px;\r\n\r\n &:first-child {\r\n border-left: 1px solid var(--border, #cecece);\r\n }\r\n\r\n &:not(:last-child) {\r\n border-right: 1px solid var(--border, #cecece);\r\n }\r\n\r\n &:last-child {\r\n border-right: 1px solid var(--border, #cecece);\r\n }\r\n\r\n .no-cell-content {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n width: 100%;\r\n height: 100%;\r\n position: relative;\r\n }\r\n\r\n .no-left-section {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n flex: 1;\r\n min-width: 0;\r\n }\r\n\r\n .tree-lines {\r\n position: absolute;\r\n left: 0;\r\n top: 0;\r\n bottom: 0;\r\n width: 100%;\r\n pointer-events: none;\r\n z-index: 0;\r\n }\r\n\r\n .tree-line-vertical {\r\n position: absolute;\r\n width: 1px;\r\n background: var(--border, #d0d0d0);\r\n \r\n // 祖先贯穿线(从顶部到底部)\r\n &.ancestor {\r\n top: 0;\r\n bottom: 0;\r\n }\r\n \r\n &.current {\r\n top: 0;\r\n bottom: 0; // 默认贯穿整行,连接兄弟节点\r\n \r\n // 如果是最后一个子节点,垂直线只到中间(└ 形状)\r\n &.is-last-child {\r\n bottom: 50%;\r\n }\r\n }\r\n \r\n // 父节点到子节点的连接线(从中间向下)\r\n &.parent-to-child {\r\n top: 50%;\r\n bottom: 0;\r\n }\r\n }\r\n\r\n .tree-line-horizontal {\r\n position: absolute;\r\n top: 50%;\r\n height: 1px;\r\n background: var(--border, #d0d0d0);\r\n }\r\n\r\n .collapse-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 22px;\r\n height: 22px;\r\n padding: 0;\r\n border: none;\r\n background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);\r\n color: #6c757d;\r\n cursor: pointer;\r\n border-radius: 6px;\r\n transition: all 0.2s ease;\r\n flex-shrink: 0;\r\n position: relative;\r\n z-index: 1;\r\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.8);\r\n\r\n svg {\r\n width: 16px;\r\n height: 16px;\r\n transition: all 0.2s ease;\r\n }\r\n\r\n // 箭头旋转动画 - 展开状态向下\r\n .arrow {\r\n transform-origin: center;\r\n transition: transform 0.2s ease;\r\n transform: rotate(90deg);\r\n }\r\n\r\n // 折叠状态:箭头向右\r\n &.collapsed .arrow {\r\n transform: rotate(0deg);\r\n }\r\n\r\n // 悬停效果\r\n &:hover {\r\n background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);\r\n color: #495057;\r\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.9);\r\n \r\n svg {\r\n transform: scale(1.1);\r\n }\r\n }\r\n\r\n // 激活状态\r\n &:active {\r\n background: linear-gradient(135deg, #dee2e6 0%, #ced4da 100%);\r\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), inset 0 1px 2px rgba(0, 0, 0, 0.1);\r\n \r\n svg {\r\n transform: scale(0.95);\r\n }\r\n }\r\n\r\n // 聚焦状态\r\n &:focus-visible {\r\n outline: 2px solid var(--primary, #3370ff);\r\n outline-offset: 2px;\r\n }\r\n }\r\n\r\n .collapse-placeholder {\r\n width: 22px;\r\n height: 22px;\r\n flex-shrink: 0;\r\n position: relative;\r\n z-index: 1;\r\n // 透明背景,不遮挡横线\r\n }\r\n\r\n .no-text {\r\n flex: 1;\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n font-size: 12px;\r\n font-weight: 500;\r\n color: var(--text-primary, #333333);\r\n position: relative;\r\n z-index: 1;\r\n }\r\n\r\n .action-buttons {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n opacity: 0;\r\n transition: opacity 0.2s ease;\r\n }\r\n\r\n &:hover .action-buttons {\r\n opacity: 1;\r\n }\r\n\r\n .action-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 22px;\r\n height: 22px;\r\n padding: 0;\r\n border: none;\r\n background: transparent;\r\n color: var(--text-secondary, #888);\r\n cursor: pointer;\r\n border-radius: 4px;\r\n transition: all 0.2s ease;\r\n flex-shrink: 0;\r\n\r\n svg {\r\n width: 14px;\r\n height: 14px;\r\n }\r\n\r\n &:hover {\r\n background: var(--hover-bg, rgba(0, 0, 0, 0.06));\r\n }\r\n\r\n &:active {\r\n transform: scale(0.95);\r\n }\r\n\r\n &.add-btn:hover {\r\n color: var(--success, #52c41a);\r\n background: rgba(82, 196, 26, 0.1);\r\n }\r\n\r\n &.delete-btn:hover {\r\n color: var(--danger, #ff4d4f);\r\n background: rgba(255, 77, 79, 0.1);\r\n }\r\n }\r\n }\r\n\r\n .cell {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 12px;\r\n border-top: none;\r\n border-bottom: none;\r\n position: relative;\r\n color: var(--text-primary, #333333);\r\n\r\n &::before {\r\n content: '';\r\n position: absolute;\r\n left: -1px;\r\n top: 0;\r\n bottom: 0;\r\n width: 1px;\r\n background: var(--border, #cecece);\r\n }\r\n\r\n &:first-child {\r\n border-left: 1px solid var(--border, #cecece);\r\n\r\n &::before {\r\n display: none;\r\n }\r\n }\r\n\r\n &:not(:last-child) {\r\n border-right: 1px solid var(--border, #cecece);\r\n }\r\n\r\n &:last-child {\r\n border-right: 1px solid var(--border, #cecece);\r\n }\r\n }\r\n}\r\n</style>","<template>\r\n <div v-if='showRow' @mouseover=\"hoverActive()\" @mouseleave=\"hoverInactive()\" :class=\"{ active: hover }\">\r\n <div class=\"row\" @dblclick=\"setEditTask(row)\" v-bind:style=\"{ height: rowHeight + 'px' }\">\r\n <template v-for='(header, headerIndex) in headers'>\r\n <div \r\n class=\"cellNo\" \r\n :key=\"headerIndex\" \r\n :columnindex=\"headerIndex\"\r\n v-if=\"header.property === 'no'\" \r\n v-bind:style=\"{\r\n minWidth: header.width + 'px',\r\n maxWidth: header.width + 'px',\r\n height: rowHeight + 'px'\r\n }\">\r\n <div class=\"no-cell-content\">\r\n <!-- 树形连线 -->\r\n <div class=\"tree-lines\">\r\n <!-- 祖先节点的贯穿线(连接兄弟节点) -->\r\n <div \r\n v-for=\"level in getAncestorLines\" \r\n :key=\"'ancestor-' + level\" \r\n class=\"tree-line-vertical ancestor\"\r\n :style=\"{ left: level * 16 + 8 + 'px' }\"\r\n ></div>\r\n \r\n <!-- 顶级节点有子节点且未折叠时,显示向下的竖线 -->\r\n <div \r\n v-if=\"row.treeLevel === 1 && hasChildren && !isCollapsed\"\r\n class=\"tree-line-vertical parent-to-child\"\r\n :style=\"{ left: row.treeLevel * 16 + 8 + 'px' }\"\r\n ></div>\r\n \r\n <!-- 子节点的连接线 -->\r\n <template v-if=\"row.treeLevel && row.treeLevel > 1\">\r\n <!-- 当前节点的垂直线 -->\r\n <div \r\n class=\"tree-line-vertical current\"\r\n :class=\"{ 'is-last-child': isLastChild }\"\r\n :style=\"{ left: (row.treeLevel - 1) * 16 + 8 + 'px' }\"\r\n ></div>\r\n \r\n <!-- 水平线(连接到当前节点) -->\r\n <div \r\n class=\"tree-line-horizontal\"\r\n :style=\"{ left: (row.treeLevel - 1) * 16 + 8 + 'px', width: '32px' }\"\r\n ></div>\r\n \r\n <!-- 如果当前节点有子节点且未折叠,显示向下的竖线 -->\r\n <div \r\n v-if=\"hasChildren && !isCollapsed\"\r\n class=\"tree-line-vertical parent-to-child\"\r\n :style=\"{ left: row.treeLevel * 16 + 8 + 'px' }\"\r\n ></div>\r\n </template>\r\n </div>\r\n \r\n <!-- 左侧:折叠按钮 + 序号 -->\r\n <div class=\"no-left-section\" :style=\"{ paddingLeft: (row.treeLevel || 0) * 16 + 'px' }\">\r\n <!-- 折叠/展开按钮 -->\r\n <button \r\n v-if=\"hasChildren\" \r\n @click.stop=\"toggleCollapse\" \r\n class=\"collapse-btn\"\r\n :class=\"{ collapsed: isCollapsed }\"\r\n :title=\"isCollapsed ? '展开' : '折叠'\"\r\n >\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\">\r\n <!-- 三角形箭头 -->\r\n <path \r\n class=\"arrow\" \r\n d=\"M5 4 L14 9 L5 14 Z\" \r\n fill=\"currentColor\"\r\n />\r\n </svg>\r\n </button>\r\n <!-- 无子节点时的占位空间(透明,不遮挡横线) -->\r\n <span v-else class=\"collapse-placeholder\"></span>\r\n \r\n <!-- 序号 -->\r\n <span class=\"no-text\">{{ row.no }}</span>\r\n </div>\r\n \r\n <!-- 右侧:操作按钮(鼠标悬停显示) -->\r\n <div class=\"action-buttons\">\r\n <button \r\n @click.stop=\"setSubTask(row)\" \r\n class=\"action-btn add-btn\"\r\n :title=\"'添加子任务'\"\r\n >\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"currentColor\">\r\n <path d=\"M8 2a.5.5 0 01.5.5v5h5a.5.5 0 010 1h-5v5a.5.5 0 01-1 0v-5h-5a.5.5 0 010-1h5v-5A.5.5 0 018 2z\"/>\r\n </svg>\r\n </button>\r\n <button \r\n @click.stop=\"setRemoveTask(row)\" \r\n class=\"action-btn delete-btn\"\r\n :title=\"'删除任务'\"\r\n >\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"currentColor\">\r\n <path d=\"M5.5 5.5A.5.5 0 016 6v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm2.5 0a.5.5 0 01.5.5v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm3 .5a.5.5 0 00-1 0v6a.5.5 0 001 0V6z\"/>\r\n <path fill-rule=\"evenodd\" d=\"M14.5 3a1 1 0 01-1 1H13v9a2 2 0 01-2 2H5a2 2 0 01-2-2V4h-.5a1 1 0 01-1-1V2a1 1 0 011-1H6a1 1 0 011-1h2a1 1 0 011 1h3.5a1 1 0 011 1v1zM4.118 4L4 4.059V13a1 1 0 001 1h6a1 1 0 001-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z\"/>\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n <div \r\n v-else \r\n v-show=\"header.show\" \r\n class=\"cell\" \r\n :key=\"headerIndex + '-header'\"\r\n :columnindex=\"headerIndex\"\r\n :style=\"{ minWidth: header.width + 'px', maxWidth: header.width + 'px', height: rowHeight + 'px' }\">\r\n {{ checkField(row, header.property) }}\r\n </div>\r\n </template>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, onMounted, computed, inject, watch } from 'vue';\r\nimport { store, mutations } from '../Store';\r\nimport sharedState from '../ShareState';\r\n\r\nexport default defineComponent({\r\n props: {\r\n headers: {\r\n type: Array as () => any[],\r\n default: () => []\r\n },\r\n rowHeight: {\r\n type: Number,\r\n default: 0\r\n },\r\n row: {\r\n type: Object as () => Record<string, any>,\r\n default: () => ({})\r\n }\r\n },\r\n setup(props) {\r\n const showRow = ref(true);\r\n const hover = ref(false);\r\n const addTips = '添加子任务';\r\n const removeTips = '删除当前任务';\r\n\r\n const mapFields = computed(() => store.mapFields);\r\n const subTask = computed(() => store.subTask);\r\n const collapsedTasks = computed(() => store.collapsedTasks);\r\n\r\n const barHover = inject('barHover') as ((rowId: any, hover: boolean) => void) | undefined;\r\n const addRootTask = inject('addRootTask') as ((row: any) => void) | undefined;\r\n\r\n // 判断当前任务是否有子任务\r\n const hasChildren = computed(() => {\r\n const currentId = props.row[mapFields.value['id']];\r\n return store.tasks.some(task => task[mapFields.value['parentId']] === currentId);\r\n });\r\n\r\n // 判断当前任务是否已折叠\r\n const isCollapsed = computed(() => {\r\n const currentId = props.row[mapFields.value['id']];\r\n return collapsedTasks.value.has(currentId);\r\n });\r\n \r\n // 判断是否是最后一个子节点\r\n const isLastChild = computed(() => {\r\n const parentId = props.row[mapFields.value['parentId']];\r\n if (!parentId || parentId === '0') return false;\r\n \r\n const siblings = store.tasks.filter(task => \r\n task[mapFields.value['parentId']] === parentId\r\n );\r\n \r\n if (siblings.length === 0) return false;\r\n \r\n const currentId = props.row[mapFields.value['id']];\r\n const lastSibling = siblings[siblings.length - 1];\r\n return lastSibling[mapFields.value['id']] === currentId;\r\n });\r\n \r\n // 获取需要显示祖先贯穿线的层级\r\n const getAncestorLines = computed(() => {\r\n const lines: number[] = [];\r\n const treeLevel = props.row.treeLevel || 0;\r\n \r\n if (treeLevel <= 1) return lines;\r\n \r\n // 当前节点的 current 线位置对应的层级(需要过滤掉,避免重叠)\r\n const currentLineLevel = treeLevel - 1;\r\n \r\n // 构建从根到当前节点的路径\r\n const path: any[] = [];\r\n let currentTask = props.row;\r\n \r\n while (currentTask) {\r\n path.unshift(currentTask);\r\n const parentId = currentTask[mapFields.value['parentId']];\r\n if (!parentId || parentId === '0') break;\r\n \r\n const parent = store.tasks.find(task => \r\n task[mapFields.value['id']] === parentId\r\n );\r\n if (!parent) break;\r\n currentTask = parent;\r\n }\r\n \r\n // 从直接父节点开始向上检查,遇到\"最后一个子节点\"就停止\r\n for (let i = path.length - 2; i >= 0; i--) {\r\n const node = path[i];\r\n const nodeId = node[mapFields.value['id']];\r\n const parentId = node[mapFields.value['parentId']];\r\n \r\n // 查找该节点的所有兄弟节点\r\n const siblings = store.tasks.filter(task => \r\n task[mapFields.value['parentId']] === parentId\r\n );\r\n \r\n if (siblings.length > 0) {\r\n const lastSibling = siblings[siblings.length - 1];\r\n const isLast = lastSibling[mapFields.value['id']] === nodeId;\r\n \r\n if (isLast) {\r\n // 如果这个节点是最后一个子节点,停止添加贯穿线\r\n break;\r\n } else {\r\n // 如果不是最后一个,需要显示贯穿线\r\n const nodeTreeLevel = node.treeLevel;\r\n // 过滤掉和 current 线位置相同的层级\r\n if (nodeTreeLevel && nodeTreeLevel >= 1 && nodeTreeLevel !== currentLineLevel) {\r\n lines.push(nodeTreeLevel);\r\n }\r\n }\r\n }\r\n }\r\n \r\n return lines;\r\n });\r\n \r\n // 切换折叠状态\r\n const toggleCollapse = () => {\r\n const currentId = props.row[mapFields.value['id']];\r\n mutations.toggleTaskCollapse(currentId);\r\n };\r\n\r\n onMounted(() => {\r\n\r\n });\r\n\r\n const setSubTask = mutations.setSubTask;\r\n const setEditTask = mutations.setEditTask;\r\n const setRemoveTask = mutations.setRemoveTask;\r\n\r\n const checkField = (row: Record<string, any>, property: string) => {\r\n if (mapFields.value[property]) {\r\n return row[mapFields.value[property]];\r\n } else if (row[property]) {\r\n return row[property];\r\n }\r\n return null;\r\n };\r\n\r\n watch(() => sharedState.highlightedId, (newId) => {\r\n if (props.row[mapFields.value['id']] === newId) {\r\n hover.value = true;\r\n } else {\r\n hover.value = false;\r\n }\r\n });\r\n\r\n const hoverActive = () => {\r\n sharedState.triggerHighlight(props.row[mapFields.value.id] as number|null);\r\n };\r\n\r\n const hoverInactive = () => {\r\n sharedState.triggerHighlight(null);\r\n };\r\n\r\n const handleAddRootTask = () => {\r\n if (addRootTask) {\r\n addRootTask(props.row);\r\n }\r\n };\r\n\r\n return {\r\n showRow,\r\n hover,\r\n addTips,\r\n removeTips,\r\n mapFields,\r\n subTask,\r\n hasChildren,\r\n isCollapsed,\r\n isLastChild,\r\n getAncestorLines,\r\n toggleCollapse,\r\n setSubTask,\r\n setEditTask,\r\n setRemoveTask,\r\n checkField,\r\n hoverActive,\r\n hoverInactive,\r\n addRootTask: handleAddRootTask\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.active .row {\r\n background: var(--row-hover, #FFF3A1) !important;\r\n}\r\n\r\n.row {\r\n display: flex;\r\n flex-flow: row nowrap;\r\n align-items: center;\r\n justify-content: flex-start;\r\n border-top: none;\r\n border-bottom: none;\r\n width: fit-content;\r\n background: var(--bg-content, #ffffff);\r\n color: var(--text-primary, #333333);\r\n\r\n &:first-child {\r\n border-top: 1px solid var(--border, #cecece);\r\n border-bottom: none;\r\n }\r\n\r\n &:not(:first-child:last-child) {\r\n border-right: 1px solid var(--border, #cecece);\r\n border-top: 1px solid var(--border, #cecece);\r\n border-bottom: 1px solid var(--border, #cecece);\r\n }\r\n\r\n &:last-child {\r\n border-top: none;\r\n border-bottom: 1px solid var(--border, #cecece);\r\n }\r\n\r\n .cellNo {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n font-size: 12px;\r\n border-top: none;\r\n border-bottom: none;\r\n margin: 0px 0px 0px 1px;\r\n position: relative;\r\n color: var(--text-primary, #333333);\r\n padding: 0 8px;\r\n\r\n &:first-child {\r\n border-left: 1px solid var(--border, #cecece);\r\n }\r\n\r\n &:not(:last-child) {\r\n border-right: 1px solid var(--border, #cecece);\r\n }\r\n\r\n &:last-child {\r\n border-right: 1px solid var(--border, #cecece);\r\n }\r\n\r\n .no-cell-content {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n width: 100%;\r\n height: 100%;\r\n position: relative;\r\n }\r\n\r\n .no-left-section {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n flex: 1;\r\n min-width: 0;\r\n }\r\n\r\n .tree-lines {\r\n position: absolute;\r\n left: 0;\r\n top: 0;\r\n bottom: 0;\r\n width: 100%;\r\n pointer-events: none;\r\n z-index: 0;\r\n }\r\n\r\n .tree-line-vertical {\r\n position: absolute;\r\n width: 1px;\r\n background: var(--border, #d0d0d0);\r\n \r\n // 祖先贯穿线(从顶部到底部)\r\n &.ancestor {\r\n top: 0;\r\n bottom: 0;\r\n }\r\n \r\n &.current {\r\n top: 0;\r\n bottom: 0; // 默认贯穿整行,连接兄弟节点\r\n \r\n // 如果是最后一个子节点,垂直线只到中间(└ 形状)\r\n &.is-last-child {\r\n bottom: 50%;\r\n }\r\n }\r\n \r\n // 父节点到子节点的连接线(从中间向下)\r\n &.parent-to-child {\r\n top: 50%;\r\n bottom: 0;\r\n }\r\n }\r\n\r\n .tree-line-horizontal {\r\n position: absolute;\r\n top: 50%;\r\n height: 1px;\r\n background: var(--border, #d0d0d0);\r\n }\r\n\r\n .collapse-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 22px;\r\n height: 22px;\r\n padding: 0;\r\n border: none;\r\n background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);\r\n color: #6c757d;\r\n cursor: pointer;\r\n border-radius: 6px;\r\n transition: all 0.2s ease;\r\n flex-shrink: 0;\r\n position: relative;\r\n z-index: 1;\r\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.8);\r\n\r\n svg {\r\n width: 16px;\r\n height: 16px;\r\n transition: all 0.2s ease;\r\n }\r\n\r\n // 箭头旋转动画 - 展开状态向下\r\n .arrow {\r\n transform-origin: center;\r\n transition: transform 0.2s ease;\r\n transform: rotate(90deg);\r\n }\r\n\r\n // 折叠状态:箭头向右\r\n &.collapsed .arrow {\r\n transform: rotate(0deg);\r\n }\r\n\r\n // 悬停效果\r\n &:hover {\r\n background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);\r\n color: #495057;\r\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.9);\r\n \r\n svg {\r\n transform: scale(1.1);\r\n }\r\n }\r\n\r\n // 激活状态\r\n &:active {\r\n background: linear-gradient(135deg, #dee2e6 0%, #ced4da 100%);\r\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), inset 0 1px 2px rgba(0, 0, 0, 0.1);\r\n \r\n svg {\r\n transform: scale(0.95);\r\n }\r\n }\r\n\r\n // 聚焦状态\r\n &:focus-visible {\r\n outline: 2px solid var(--primary, #3370ff);\r\n outline-offset: 2px;\r\n }\r\n }\r\n\r\n .collapse-placeholder {\r\n width: 22px;\r\n height: 22px;\r\n flex-shrink: 0;\r\n position: relative;\r\n z-index: 1;\r\n // 透明背景,不遮挡横线\r\n }\r\n\r\n .no-text {\r\n flex: 1;\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n font-size: 12px;\r\n font-weight: 500;\r\n color: var(--text-primary, #333333);\r\n position: relative;\r\n z-index: 1;\r\n }\r\n\r\n .action-buttons {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n opacity: 0;\r\n transition: opacity 0.2s ease;\r\n }\r\n\r\n &:hover .action-buttons {\r\n opacity: 1;\r\n }\r\n\r\n .action-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 22px;\r\n height: 22px;\r\n padding: 0;\r\n border: none;\r\n background: transparent;\r\n color: var(--text-secondary, #888);\r\n cursor: pointer;\r\n border-radius: 4px;\r\n transition: all 0.2s ease;\r\n flex-shrink: 0;\r\n\r\n svg {\r\n width: 14px;\r\n height: 14px;\r\n }\r\n\r\n &:hover {\r\n background: var(--hover-bg, rgba(0, 0, 0, 0.06));\r\n }\r\n\r\n &:active {\r\n transform: scale(0.95);\r\n }\r\n\r\n &.add-btn:hover {\r\n color: var(--success, #52c41a);\r\n background: rgba(82, 196, 26, 0.1);\r\n }\r\n\r\n &.delete-btn:hover {\r\n color: var(--danger, #ff4d4f);\r\n background: rgba(255, 77, 79, 0.1);\r\n }\r\n }\r\n }\r\n\r\n .cell {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 12px;\r\n border-top: none;\r\n border-bottom: none;\r\n position: relative;\r\n color: var(--text-primary, #333333);\r\n\r\n &::before {\r\n content: '';\r\n position: absolute;\r\n left: -1px;\r\n top: 0;\r\n bottom: 0;\r\n width: 1px;\r\n background: var(--border, #cecece);\r\n }\r\n\r\n &:first-child {\r\n border-left: 1px solid var(--border, #cecece);\r\n\r\n &::before {\r\n display: none;\r\n }\r\n }\r\n\r\n &:not(:last-child) {\r\n border-right: 1px solid var(--border, #cecece);\r\n }\r\n\r\n &:last-child {\r\n border-right: 1px solid var(--border, #cecece);\r\n }\r\n }\r\n}\r\n</style>","<template>\r\n <div>\r\n <template v-for=\"item in filterTask\" :key=\"item.id + '_taskrow'\">\r\n <template v-if=\"headers\">\r\n <TaskRow \r\n :headers=\"headers\" \r\n :rowHeight=\"rowHeight\" \r\n :row=\"item\" \r\n />\r\n </template>\r\n </template>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, computed, watch } from 'vue';\r\nimport { store, mutations } from '../Store';\r\nimport TaskRow from './TaskRow.vue';\r\n\r\nexport default defineComponent({\r\n props: {\r\n headers: Array as () => any[],\r\n rowHeight: Number as () => number,\r\n tasks: Array as () => any[]\r\n },\r\n components: {\r\n TaskRow\r\n },\r\n setup(props) {\r\n const hiddenTask = ref<Array<any>>([]);\r\n const mapFields = computed(() => store.mapFields);\r\n const collapsedTasks = computed(() => store.collapsedTasks);\r\n \r\n // 获取所有被折叠的子任务\r\n const getAllCollapsedChildren = (parentId: any): Set<any> => {\r\n const collapsedChildren = new Set<any>();\r\n const tasks = props.tasks || store.tasks;\r\n \r\n const collectChildren = (pid: any) => {\r\n const children = tasks.filter(task => task[mapFields.value['parentId']] === pid);\r\n children.forEach(child => {\r\n const childId = child[mapFields.value['id']];\r\n collapsedChildren.add(childId);\r\n // 递归收集所有子孙任务\r\n collectChildren(childId);\r\n });\r\n };\r\n \r\n collectChildren(parentId);\r\n return collapsedChildren;\r\n };\r\n \r\n // 优化:使用Set提高查找性能\r\n const hiddenTaskIds = computed(() => {\r\n return new Set(hiddenTask.value.map(obj => obj[mapFields.value['id']]));\r\n });\r\n\r\n const filterTask = computed(() => {\r\n const hiddenIds = hiddenTaskIds.value;\r\n const tasks = store.tasks.filter(task => !hiddenIds.has(task[mapFields.value['id']]));\r\n \r\n // 过滤折叠的子任务\r\n const allCollapsedIds = new Set<any>();\r\n collapsedTasks.value.forEach(collapsedId => {\r\n const children = getAllCollapsedChildren(collapsedId);\r\n children.forEach(childId => allCollapsedIds.add(childId));\r\n });\r\n \r\n return tasks.filter(task => !allCollapsedIds.has(task[mapFields.value['id']]));\r\n });\r\n\r\n const expandRow = computed({\r\n get: () => store.expandRow,\r\n set: (newValue) => {\r\n mutations.setExpandRow(newValue);\r\n }\r\n });\r\n\r\n watch(expandRow, (newVal) => {\r\n hiddenTask.value = [];\r\n recursionRow(newVal.pid);\r\n });\r\n\r\n const recursionRow = (id: any) => {\r\n // 检查 props.tasks 是否存在,如果存在则进行过滤,否则返回空数组\r\n let findRows = props.tasks ? props.tasks.filter(obj => obj[mapFields.value['parentId']] === id) : [];\r\n if (findRows && findRows.length > 0) {\r\n for (let i = 0; i < findRows.length; i++) {\r\n if (expandRow.value.expand === false) {\r\n hiddenTask.value.push(findRows[i]);\r\n }\r\n recursionRow(findRows[i][mapFields.value['id']]);\r\n }\r\n }\r\n };\r\n\r\n return {\r\n filterTask,\r\n expandRow,\r\n recursionRow,\r\n mapFields\r\n };\r\n }\r\n});\r\n</script>","<template>\r\n <div>\r\n <template v-for=\"item in filterTask\" :key=\"item.id + '_taskrow'\">\r\n <template v-if=\"headers\">\r\n <TaskRow \r\n :headers=\"headers\" \r\n :rowHeight=\"rowHeight\" \r\n :row=\"item\" \r\n />\r\n </template>\r\n </template>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, computed, watch } from 'vue';\r\nimport { store, mutations } from '../Store';\r\nimport TaskRow from './TaskRow.vue';\r\n\r\nexport default defineComponent({\r\n props: {\r\n headers: Array as () => any[],\r\n rowHeight: Number as () => number,\r\n tasks: Array as () => any[]\r\n },\r\n components: {\r\n TaskRow\r\n },\r\n setup(props) {\r\n const hiddenTask = ref<Array<any>>([]);\r\n const mapFields = computed(() => store.mapFields);\r\n const collapsedTasks = computed(() => store.collapsedTasks);\r\n \r\n // 获取所有被折叠的子任务\r\n const getAllCollapsedChildren = (parentId: any): Set<any> => {\r\n const collapsedChildren = new Set<any>();\r\n const tasks = props.tasks || store.tasks;\r\n \r\n const collectChildren = (pid: any) => {\r\n const children = tasks.filter(task => task[mapFields.value['parentId']] === pid);\r\n children.forEach(child => {\r\n const childId = child[mapFields.value['id']];\r\n collapsedChildren.add(childId);\r\n // 递归收集所有子孙任务\r\n collectChildren(childId);\r\n });\r\n };\r\n \r\n collectChildren(parentId);\r\n return collapsedChildren;\r\n };\r\n \r\n // 优化:使用Set提高查找性能\r\n const hiddenTaskIds = computed(() => {\r\n return new Set(hiddenTask.value.map(obj => obj[mapFields.value['id']]));\r\n });\r\n\r\n const filterTask = computed(() => {\r\n const hiddenIds = hiddenTaskIds.value;\r\n const tasks = store.tasks.filter(task => !hiddenIds.has(task[mapFields.value['id']]));\r\n \r\n // 过滤折叠的子任务\r\n const allCollapsedIds = new Set<any>();\r\n collapsedTasks.value.forEach(collapsedId => {\r\n const children = getAllCollapsedChildren(collapsedId);\r\n children.forEach(childId => allCollapsedIds.add(childId));\r\n });\r\n \r\n return tasks.filter(task => !allCollapsedIds.has(task[mapFields.value['id']]));\r\n });\r\n\r\n const expandRow = computed({\r\n get: () => store.expandRow,\r\n set: (newValue) => {\r\n mutations.setExpandRow(newValue);\r\n }\r\n });\r\n\r\n watch(expandRow, (newVal) => {\r\n hiddenTask.value = [];\r\n recursionRow(newVal.pid);\r\n });\r\n\r\n const recursionRow = (id: any) => {\r\n // 检查 props.tasks 是否存在,如果存在则进行过滤,否则返回空数组\r\n let findRows = props.tasks ? props.tasks.filter(obj => obj[mapFields.value['parentId']] === id) : [];\r\n if (findRows && findRows.length > 0) {\r\n for (let i = 0; i < findRows.length; i++) {\r\n if (expandRow.value.expand === false) {\r\n hiddenTask.value.push(findRows[i]);\r\n }\r\n recursionRow(findRows[i][mapFields.value['id']]);\r\n }\r\n }\r\n };\r\n\r\n return {\r\n filterTask,\r\n expandRow,\r\n recursionRow,\r\n mapFields\r\n };\r\n }\r\n});\r\n</script>","<template>\r\n <div ref=\"taskContent\" class=\"content\" @scroll=\"scroll()\" @mouseover=\"mouseover()\">\r\n <div class=\"content-inner\" :style=\"{ minHeight: contentHeight + 'px' }\">\r\n <TaskRecursionRow :headers='headers' :rowHeight='rowHeight' :tasks='tasks'></TaskRecursionRow>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, watch, computed, onMounted, onUnmounted } from 'vue';\r\nimport { store } from '../Store';\r\nimport { useScrollState } from '../ShareState';\r\nimport TaskRecursionRow from './TaskRecursionRow.vue';\r\n\r\nexport default defineComponent({\r\n props: {\r\n headers: {\r\n type: Array as () => any[],\r\n default: () => []\r\n },\r\n rowHeight: {\r\n type: Number,\r\n default: 0\r\n }\r\n },\r\n components: {\r\n TaskRecursionRow\r\n },\r\n setup(props) {\r\n const tasks = computed(() => store.tasks);\r\n const { scrollTop, scrollFlag, setScrollTop, setScrollFlag } = useScrollState();\r\n const taskContent = ref<HTMLDivElement | null>(null);\r\n const mapFields = computed(() => store.mapFields);\r\n \r\n // 计算内容高度,与右侧保持一致\r\n const contentHeight = computed(() => {\r\n return tasks.value.length * props.rowHeight;\r\n });\r\n \r\n const getRootNode = () => {\r\n return tasks.value.filter((obj: any) => obj[mapFields.value.parentId] === '0');\r\n };\r\n\r\n watch(scrollTop, (newValue) => {\r\n if (!scrollFlag.value && taskContent.value) {\r\n taskContent.value.scrollTop = newValue;\r\n }\r\n });\r\n\r\n // 优化:使用requestAnimationFrame优化滚动性能\r\n let rafId: number | null = null;\r\n const scroll = () => {\r\n if (rafId) {\r\n cancelAnimationFrame(rafId);\r\n }\r\n rafId = requestAnimationFrame(() => {\r\n if (taskContent.value) {\r\n setScrollFlag(true); // 标记当前面板为主动滚动\r\n setScrollTop(taskContent.value.scrollTop);\r\n }\r\n rafId = null;\r\n });\r\n };\r\n\r\n const mouseover = () => {\r\n // 鼠标悬停时不改变滚动标志,让滚动事件处理\r\n };\r\n\r\n // 动态同步滚动区域高度\r\n const syncScrollHeight = () => {\r\n if (taskContent.value) {\r\n // 查找右侧的滚动容器\r\n const rightContent = document.querySelector('.table .content') as HTMLElement;\r\n if (rightContent) {\r\n // 检测右侧是否有水平滚动条\r\n const hasHorizontalScrollbar = rightContent.scrollWidth > rightContent.clientWidth;\r\n \r\n // 动态调整左侧的padding-bottom\r\n if (hasHorizontalScrollbar) {\r\n // 如果右侧有水平滚动条,给左侧添加相应的padding\r\n taskContent.value.style.paddingBottom = '20px';\r\n } else {\r\n // 如果右侧没有水平滚动条,移除左侧的padding\r\n taskContent.value.style.paddingBottom = '0px';\r\n }\r\n }\r\n }\r\n };\r\n\r\n // 监听任务变化,重新同步高度\r\n watch(tasks, () => {\r\n setTimeout(syncScrollHeight, 50);\r\n });\r\n \r\n // 监听窗口大小变化,重新同步高度\r\n const handleResize = () => {\r\n setTimeout(syncScrollHeight, 50);\r\n };\r\n \r\n onMounted(() => {\r\n if (taskContent.value) {\r\n // 监听滚动位置的变化 \r\n taskContent.value.scrollTop = scrollTop.value;\r\n \r\n // 延迟同步高度,确保DOM已渲染完成\r\n setTimeout(syncScrollHeight, 100);\r\n \r\n // 监听窗口大小变化\r\n window.addEventListener('resize', handleResize);\r\n }\r\n });\r\n \r\n onUnmounted(() => {\r\n window.removeEventListener('resize', handleResize);\r\n });\r\n\r\n return {\r\n tasks,\r\n taskContent,\r\n scrollFlag,\r\n mapFields,\r\n setScrollFlag,\r\n getRootNode,\r\n scroll,\r\n mouseover,\r\n contentHeight\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.content {\r\n height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n position: relative;\r\n box-sizing: border-box;\r\n \r\n .content-inner {\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n\r\n .moveToBar {\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: flex-start;\r\n }\r\n\r\n .expandBar {\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: flex-start;\r\n }\r\n}\r\n</style>","<template>\r\n <div ref=\"taskContent\" class=\"content\" @scroll=\"scroll()\" @mouseover=\"mouseover()\">\r\n <div class=\"content-inner\" :style=\"{ minHeight: contentHeight + 'px' }\">\r\n <TaskRecursionRow :headers='headers' :rowHeight='rowHeight' :tasks='tasks'></TaskRecursionRow>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, watch, computed, onMounted, onUnmounted } from 'vue';\r\nimport { store } from '../Store';\r\nimport { useScrollState } from '../ShareState';\r\nimport TaskRecursionRow from './TaskRecursionRow.vue';\r\n\r\nexport default defineComponent({\r\n props: {\r\n headers: {\r\n type: Array as () => any[],\r\n default: () => []\r\n },\r\n rowHeight: {\r\n type: Number,\r\n default: 0\r\n }\r\n },\r\n components: {\r\n TaskRecursionRow\r\n },\r\n setup(props) {\r\n const tasks = computed(() => store.tasks);\r\n const { scrollTop, scrollFlag, setScrollTop, setScrollFlag } = useScrollState();\r\n const taskContent = ref<HTMLDivElement | null>(null);\r\n const mapFields = computed(() => store.mapFields);\r\n \r\n // 计算内容高度,与右侧保持一致\r\n const contentHeight = computed(() => {\r\n return tasks.value.length * props.rowHeight;\r\n });\r\n \r\n const getRootNode = () => {\r\n return tasks.value.filter((obj: any) => obj[mapFields.value.parentId] === '0');\r\n };\r\n\r\n watch(scrollTop, (newValue) => {\r\n if (!scrollFlag.value && taskContent.value) {\r\n taskContent.value.scrollTop = newValue;\r\n }\r\n });\r\n\r\n // 优化:使用requestAnimationFrame优化滚动性能\r\n let rafId: number | null = null;\r\n const scroll = () => {\r\n if (rafId) {\r\n cancelAnimationFrame(rafId);\r\n }\r\n rafId = requestAnimationFrame(() => {\r\n if (taskContent.value) {\r\n setScrollFlag(true); // 标记当前面板为主动滚动\r\n setScrollTop(taskContent.value.scrollTop);\r\n }\r\n rafId = null;\r\n });\r\n };\r\n\r\n const mouseover = () => {\r\n // 鼠标悬停时不改变滚动标志,让滚动事件处理\r\n };\r\n\r\n // 动态同步滚动区域高度\r\n const syncScrollHeight = () => {\r\n if (taskContent.value) {\r\n // 查找右侧的滚动容器\r\n const rightContent = document.querySelector('.table .content') as HTMLElement;\r\n if (rightContent) {\r\n // 检测右侧是否有水平滚动条\r\n const hasHorizontalScrollbar = rightContent.scrollWidth > rightContent.clientWidth;\r\n \r\n // 动态调整左侧的padding-bottom\r\n if (hasHorizontalScrollbar) {\r\n // 如果右侧有水平滚动条,给左侧添加相应的padding\r\n taskContent.value.style.paddingBottom = '20px';\r\n } else {\r\n // 如果右侧没有水平滚动条,移除左侧的padding\r\n taskContent.value.style.paddingBottom = '0px';\r\n }\r\n }\r\n }\r\n };\r\n\r\n // 监听任务变化,重新同步高度\r\n watch(tasks, () => {\r\n setTimeout(syncScrollHeight, 50);\r\n });\r\n \r\n // 监听窗口大小变化,重新同步高度\r\n const handleResize = () => {\r\n setTimeout(syncScrollHeight, 50);\r\n };\r\n \r\n onMounted(() => {\r\n if (taskContent.value) {\r\n // 监听滚动位置的变化 \r\n taskContent.value.scrollTop = scrollTop.value;\r\n \r\n // 延迟同步高度,确保DOM已渲染完成\r\n setTimeout(syncScrollHeight, 100);\r\n \r\n // 监听窗口大小变化\r\n window.addEventListener('resize', handleResize);\r\n }\r\n });\r\n \r\n onUnmounted(() => {\r\n window.removeEventListener('resize', handleResize);\r\n });\r\n\r\n return {\r\n tasks,\r\n taskContent,\r\n scrollFlag,\r\n mapFields,\r\n setScrollFlag,\r\n getRootNode,\r\n scroll,\r\n mouseover,\r\n contentHeight\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.content {\r\n height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n position: relative;\r\n box-sizing: border-box;\r\n \r\n .content-inner {\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n\r\n .moveToBar {\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: flex-start;\r\n }\r\n\r\n .expandBar {\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: flex-start;\r\n }\r\n}\r\n</style>","!function(e,i){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=i():\"function\"==typeof define&&define.amd?define(i):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).dayjs_plugin_isBetween=i()}(this,(function(){\"use strict\";return function(e,i,t){i.prototype.isBetween=function(e,i,s,f){var n=t(e),o=t(i),r=\"(\"===(f=f||\"()\")[0],u=\")\"===f[1];return(r?this.isAfter(n,s):!this.isBefore(n,s))&&(u?this.isBefore(o,s):!this.isAfter(o,s))||(r?this.isBefore(n,s):!this.isAfter(n,s))&&(u?this.isAfter(o,s):!this.isBefore(o,s))}}}));","<template>\r\n <div class=\"table\">\r\n <div class=\"header\" :style=\"{ height: `${headersHeight}px` }\">\r\n <svg ref=\"addTaskSvg\" t=\"1647915776075\" @click=\"setRootTask({})\" class=\"addRootTask\" viewBox=\"0 0 1024 1024\"\r\n version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"3147\" width=\"200\" height=\"200\">\r\n <path\r\n d=\"M864 0H160C70.4 0 0 70.4 0 160v704c0 89.6 70.4 160 160 160h704c89.6 0 160-70.4 160-160V160c0-89.6-70.4-160-160-160z m96 864c0 54.4-41.6 96-96 96H160c-54.4 0-96-41.6-96-96V160c0-54.4 41.6-96 96-96h704c54.4 0 96 41.6 96 96v704z\"\r\n fill=\"currentColor\" p-id=\"3148\"></path>\r\n <path\r\n d=\"M768 480h-224V256c0-19.2-12.8-32-32-32s-32 12.8-32 32v224H256c-19.2 0-32 12.8-32 32s12.8 32 32 32h224v224c0 19.2 12.8 32 32 32s32-12.8 32-32v-224h224c19.2 0 32-12.8 32-32s-12.8-32-32-32z\"\r\n p-id=\"3149\" fill=\"currentColor\"></path>\r\n </svg>\r\n <svg ref=\"jumpTodaySvg\" t=\"1647262391689\" @click=\"scrollToToday()\" class=\"jumpToToday\"\r\n viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"4965\" width=\"200\"\r\n height=\"200\">\r\n <path\r\n d=\"M753.6 222.4h24c19.2 0 33.6-14.4 33.6-32V57.6c0-19.2-14.4-33.6-33.6-33.6h-24c-19.2 0-33.6 14.4-33.6 33.6v131.2c0 19.2 14.4 33.6 33.6 33.6zM251.2 222.4h24c19.2 0 33.6-14.4 33.6-32V57.6c0-19.2-14.4-33.6-33.6-33.6h-24c-19.2 0-33.6 14.4-33.6 33.6v131.2c0 19.2 14.4 33.6 33.6 33.6z\"\r\n fill=\"currentColor\" p-id=\"4966\"></path>\r\n <path\r\n d=\"M928 134.4h-68.8v56c0 41.6-33.6 76.8-80 76.8h-24c-43.2 0-80-33.6-80-76.8V134.4h-320v56c0 41.6-33.6 76.8-80 76.8h-24c-43.2 0-80-33.6-80-76.8V134.4H105.6c-38.4 0-68.8 28.8-68.8 67.2v731.2c0 38.4 30.4 67.2 68.8 67.2h820.8c38.4 0 68.8-28.8 70.4-67.2V201.6c0-38.4-30.4-67.2-68.8-67.2zM105.6 932.8V355.2h820.8s0 577.6 1.6 577.6H105.6z\"\r\n fill=\"currentColor\" p-id=\"4967\"></path>\r\n <path d=\"M500.8 548.8l-49.6 33.6c14.4 16 33.6 41.6 60.8 75.2l54.4-35.2c-19.2-22.4-40-46.4-65.6-73.6z\"\r\n fill=\"currentColor\" p-id=\"4968\"></path>\r\n <path\r\n d=\"M553.6 451.2l14.4-14.4v-1.6H480c-51.2 68.8-118.4 121.6-196.8 155.2 11.2 12.8 25.6 28.8 41.6 54.4 80-40 142.4-89.6 188.8-148.8 43.2 59.2 102.4 107.2 180.8 144 14.4-19.2 27.2-35.2 41.6-52.8-76.8-30.4-137.6-76.8-182.4-136zM339.2 716.8h246.4c-30.4 43.2-62.4 81.6-94.4 116.8l60.8 33.6c49.6-56 89.6-108.8 123.2-155.2v-54.4h-336v59.2z\"\r\n fill=\"currentColor\" p-id=\"4969\"></path>\r\n </svg>\r\n <TaskHeader :headers='taskHeaders' />\r\n </div>\r\n <div :style=\"{ height: `calc(100% - ${headersHeight}px)` }\">\r\n <TaskContent v-if='Array.isArray(tasks) && tasks.length > 0' :headers='taskHeaders' :rowHeight='rowHeight'>\r\n </TaskContent>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, computed } from 'vue';\r\nimport TaskHeader from './TaskHeader.vue';\r\nimport TaskContent from './TaskContent.vue';\r\nimport { store, mutations } from '../Store';\r\nimport dayjs from 'dayjs';\r\nimport isBetween from 'dayjs/plugin/isBetween';\r\ndayjs.extend(isBetween);\r\nimport sharedState from '../ShareState';\r\n\r\nexport default defineComponent({\r\n props: {\r\n headersHeight: {\r\n type: Number as () => number,\r\n default: 50\r\n },\r\n rowHeight: {\r\n type: Number as () => number,\r\n default: 0\r\n }\r\n },\r\n components: {\r\n TaskHeader,\r\n TaskContent\r\n },\r\n setup() {\r\n const tasks = computed(() => store.tasks);\r\n const taskHeaders = computed(() => store.taskHeaders);\r\n const rootTask = computed({\r\n get: () => store.rootTask,\r\n set: (newValue) => {\r\n mutations.setRootTask(newValue);\r\n }\r\n });\r\n const startGanttDate = computed(() => store.startGanttDate);\r\n const endGanttDate = computed(() => store.endGanttDate);\r\n\r\n const setRootTask = mutations.setRootTask;\r\n const scrollToToday = () => {\r\n // 判断今天在选择的时间范围内\r\n let isBetween = dayjs().isBetween(startGanttDate.value, endGanttDate.value);\r\n if (isBetween) {\r\n sharedState.triggerScrollToToday();\r\n }\r\n };\r\n\r\n return {\r\n tasks,\r\n taskHeaders,\r\n rootTask,\r\n startGanttDate,\r\n endGanttDate,\r\n setRootTask,\r\n scrollToToday,\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.table {\r\n height: 100%;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n overflow-y: hidden;\r\n overflow-x: hidden;\r\n background: var(--bg-content, #ffffff);\r\n border: 1px solid var(--border, #d0d0d0);\r\n\r\n .header {\r\n height: 100%;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n position: relative;\r\n border-bottom: 1px solid var(--border, #d0d0d0);\r\n box-shadow: var(--shadow-inset, inset 0 1px 0 rgba(255, 255, 255, 0.8));\r\n\r\n .addRootTask {\r\n position: absolute;\r\n z-index: 10;\r\n bottom: 4px;\r\n right: 4px;\r\n height: 20px;\r\n width: 20px;\r\n cursor: pointer;\r\n color: var(--text-secondary, #666666);\r\n transition: all var(--transition-fast, 0.15s ease);\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n padding: 2px;\r\n\r\n &:hover {\r\n color: var(--primary, #0078d4);\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n transform: scale(1.1);\r\n }\r\n\r\n &:active {\r\n background: var(--bg-metal-pressed, linear-gradient(145deg, #e0e0e0, #f8f8f8));\r\n }\r\n }\r\n\r\n .jumpToToday {\r\n position: absolute;\r\n z-index: 10;\r\n top: 4px;\r\n right: 4px;\r\n height: 20px;\r\n width: 20px;\r\n cursor: pointer;\r\n color: var(--text-secondary, #666666);\r\n transition: all var(--transition-fast, 0.15s ease);\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n padding: 2px;\r\n\r\n &:hover {\r\n color: var(--primary, #0078d4);\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n transform: scale(1.1);\r\n }\r\n\r\n &:active {\r\n background: var(--bg-metal-pressed, linear-gradient(145deg, #e0e0e0, #f8f8f8));\r\n }\r\n }\r\n }\r\n\r\n .nodata {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n text-align: center;\r\n width: 100%;\r\n height: 100%;\r\n color: var(--text-muted, #999999);\r\n font-size: 14px;\r\n font-weight: 500;\r\n background: var(--bg-content, #ffffff);\r\n }\r\n}\r\n</style>","<template>\r\n <div class=\"table\">\r\n <div class=\"header\" :style=\"{ height: `${headersHeight}px` }\">\r\n <svg ref=\"addTaskSvg\" t=\"1647915776075\" @click=\"setRootTask({})\" class=\"addRootTask\" viewBox=\"0 0 1024 1024\"\r\n version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"3147\" width=\"200\" height=\"200\">\r\n <path\r\n d=\"M864 0H160C70.4 0 0 70.4 0 160v704c0 89.6 70.4 160 160 160h704c89.6 0 160-70.4 160-160V160c0-89.6-70.4-160-160-160z m96 864c0 54.4-41.6 96-96 96H160c-54.4 0-96-41.6-96-96V160c0-54.4 41.6-96 96-96h704c54.4 0 96 41.6 96 96v704z\"\r\n fill=\"currentColor\" p-id=\"3148\"></path>\r\n <path\r\n d=\"M768 480h-224V256c0-19.2-12.8-32-32-32s-32 12.8-32 32v224H256c-19.2 0-32 12.8-32 32s12.8 32 32 32h224v224c0 19.2 12.8 32 32 32s32-12.8 32-32v-224h224c19.2 0 32-12.8 32-32s-12.8-32-32-32z\"\r\n p-id=\"3149\" fill=\"currentColor\"></path>\r\n </svg>\r\n <svg ref=\"jumpTodaySvg\" t=\"1647262391689\" @click=\"scrollToToday()\" class=\"jumpToToday\"\r\n viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"4965\" width=\"200\"\r\n height=\"200\">\r\n <path\r\n d=\"M753.6 222.4h24c19.2 0 33.6-14.4 33.6-32V57.6c0-19.2-14.4-33.6-33.6-33.6h-24c-19.2 0-33.6 14.4-33.6 33.6v131.2c0 19.2 14.4 33.6 33.6 33.6zM251.2 222.4h24c19.2 0 33.6-14.4 33.6-32V57.6c0-19.2-14.4-33.6-33.6-33.6h-24c-19.2 0-33.6 14.4-33.6 33.6v131.2c0 19.2 14.4 33.6 33.6 33.6z\"\r\n fill=\"currentColor\" p-id=\"4966\"></path>\r\n <path\r\n d=\"M928 134.4h-68.8v56c0 41.6-33.6 76.8-80 76.8h-24c-43.2 0-80-33.6-80-76.8V134.4h-320v56c0 41.6-33.6 76.8-80 76.8h-24c-43.2 0-80-33.6-80-76.8V134.4H105.6c-38.4 0-68.8 28.8-68.8 67.2v731.2c0 38.4 30.4 67.2 68.8 67.2h820.8c38.4 0 68.8-28.8 70.4-67.2V201.6c0-38.4-30.4-67.2-68.8-67.2zM105.6 932.8V355.2h820.8s0 577.6 1.6 577.6H105.6z\"\r\n fill=\"currentColor\" p-id=\"4967\"></path>\r\n <path d=\"M500.8 548.8l-49.6 33.6c14.4 16 33.6 41.6 60.8 75.2l54.4-35.2c-19.2-22.4-40-46.4-65.6-73.6z\"\r\n fill=\"currentColor\" p-id=\"4968\"></path>\r\n <path\r\n d=\"M553.6 451.2l14.4-14.4v-1.6H480c-51.2 68.8-118.4 121.6-196.8 155.2 11.2 12.8 25.6 28.8 41.6 54.4 80-40 142.4-89.6 188.8-148.8 43.2 59.2 102.4 107.2 180.8 144 14.4-19.2 27.2-35.2 41.6-52.8-76.8-30.4-137.6-76.8-182.4-136zM339.2 716.8h246.4c-30.4 43.2-62.4 81.6-94.4 116.8l60.8 33.6c49.6-56 89.6-108.8 123.2-155.2v-54.4h-336v59.2z\"\r\n fill=\"currentColor\" p-id=\"4969\"></path>\r\n </svg>\r\n <TaskHeader :headers='taskHeaders' />\r\n </div>\r\n <div :style=\"{ height: `calc(100% - ${headersHeight}px)` }\">\r\n <TaskContent v-if='Array.isArray(tasks) && tasks.length > 0' :headers='taskHeaders' :rowHeight='rowHeight'>\r\n </TaskContent>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, computed } from 'vue';\r\nimport TaskHeader from './TaskHeader.vue';\r\nimport TaskContent from './TaskContent.vue';\r\nimport { store, mutations } from '../Store';\r\nimport dayjs from 'dayjs';\r\nimport isBetween from 'dayjs/plugin/isBetween';\r\ndayjs.extend(isBetween);\r\nimport sharedState from '../ShareState';\r\n\r\nexport default defineComponent({\r\n props: {\r\n headersHeight: {\r\n type: Number as () => number,\r\n default: 50\r\n },\r\n rowHeight: {\r\n type: Number as () => number,\r\n default: 0\r\n }\r\n },\r\n components: {\r\n TaskHeader,\r\n TaskContent\r\n },\r\n setup() {\r\n const tasks = computed(() => store.tasks);\r\n const taskHeaders = computed(() => store.taskHeaders);\r\n const rootTask = computed({\r\n get: () => store.rootTask,\r\n set: (newValue) => {\r\n mutations.setRootTask(newValue);\r\n }\r\n });\r\n const startGanttDate = computed(() => store.startGanttDate);\r\n const endGanttDate = computed(() => store.endGanttDate);\r\n\r\n const setRootTask = mutations.setRootTask;\r\n const scrollToToday = () => {\r\n // 判断今天在选择的时间范围内\r\n let isBetween = dayjs().isBetween(startGanttDate.value, endGanttDate.value);\r\n if (isBetween) {\r\n sharedState.triggerScrollToToday();\r\n }\r\n };\r\n\r\n return {\r\n tasks,\r\n taskHeaders,\r\n rootTask,\r\n startGanttDate,\r\n endGanttDate,\r\n setRootTask,\r\n scrollToToday,\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.table {\r\n height: 100%;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n overflow-y: hidden;\r\n overflow-x: hidden;\r\n background: var(--bg-content, #ffffff);\r\n border: 1px solid var(--border, #d0d0d0);\r\n\r\n .header {\r\n height: 100%;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n position: relative;\r\n border-bottom: 1px solid var(--border, #d0d0d0);\r\n box-shadow: var(--shadow-inset, inset 0 1px 0 rgba(255, 255, 255, 0.8));\r\n\r\n .addRootTask {\r\n position: absolute;\r\n z-index: 10;\r\n bottom: 4px;\r\n right: 4px;\r\n height: 20px;\r\n width: 20px;\r\n cursor: pointer;\r\n color: var(--text-secondary, #666666);\r\n transition: all var(--transition-fast, 0.15s ease);\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n padding: 2px;\r\n\r\n &:hover {\r\n color: var(--primary, #0078d4);\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n transform: scale(1.1);\r\n }\r\n\r\n &:active {\r\n background: var(--bg-metal-pressed, linear-gradient(145deg, #e0e0e0, #f8f8f8));\r\n }\r\n }\r\n\r\n .jumpToToday {\r\n position: absolute;\r\n z-index: 10;\r\n top: 4px;\r\n right: 4px;\r\n height: 20px;\r\n width: 20px;\r\n cursor: pointer;\r\n color: var(--text-secondary, #666666);\r\n transition: all var(--transition-fast, 0.15s ease);\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n padding: 2px;\r\n\r\n &:hover {\r\n color: var(--primary, #0078d4);\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n transform: scale(1.1);\r\n }\r\n\r\n &:active {\r\n background: var(--bg-metal-pressed, linear-gradient(145deg, #e0e0e0, #f8f8f8));\r\n }\r\n }\r\n }\r\n\r\n .nodata {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n text-align: center;\r\n width: 100%;\r\n height: 100%;\r\n color: var(--text-muted, #999999);\r\n font-size: 14px;\r\n font-weight: 500;\r\n background: var(--bg-content, #ffffff);\r\n }\r\n}\r\n</style>","<template>\r\n <div class=\"headerContainer\">\r\n <div v-if=\"monthHeaders && monthHeaders.length > 0\" class=\"header\">\r\n <template v-for='item in monthHeaders' :key=\"item.title\">\r\n <div class=\"headerCaption\" style=\"border-bottom:0px\" :style=\"{ width: item.width + 'px' }\"><span :style=\"{ width: item.width + 'px' }\">{{item.title}}</span></div>\r\n </template>\r\n </div>\r\n <div v-if=\"weekHeaders && weekHeaders.length > 0\" class=\"header\">\r\n <template v-for='item in weekHeaders' :key=\"item.title\">\r\n <div class=\"headerCaption\" style=\"border-top:1px\" :style=\"{ width: item.width + 'px' }\"><span :style=\"{ width: item.width + 'px' }\">{{item.title}}</span>\r\n </div>\r\n </template>\r\n </div>\r\n <div v-if=\"dayHeaders && dayHeaders.length > 0\" class=\"header\">\r\n <!-- 移除未使用的索引变量 -->\r\n <template v-for='item in dayHeaders' :key=\"item.title\">\r\n <div class=\"headerCaption\" :style=\"{ width: item.width + 'px' }\"><span :style=\"{ width: item.width + 'px' }\">{{item.title}}</span>\r\n <svg t=\"1647262391689\" v-if=\"isToday(item.fulldate)\" class=\"today\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"4965\" width=\"200\" height=\"200\"><path d=\"M753.6 222.4h24c19.2 0 33.6-14.4 33.6-32V57.6c0-19.2-14.4-33.6-33.6-33.6h-24c-19.2 0-33.6 14.4-33.6 33.6v131.2c0 19.2 14.4 33.6 33.6 33.6zM251.2 222.4h24c19.2 0 33.6-14.4 33.6-32V57.6c0-19.2-14.4-33.6-33.6-33.6h-24c-19.2 0-33.6 14.4-33.6 33.6v131.2c0 19.2 14.4 33.6 33.6 33.6z\" fill=\"#707070\" p-id=\"4966\"></path><path d=\"M928 134.4h-68.8v56c0 41.6-33.6 76.8-80 76.8h-24c-43.2 0-80-33.6-80-76.8V134.4h-320v56c0 41.6-33.6 76.8-80 76.8h-24c-43.2 0-80-33.6-80-76.8V134.4H105.6c-38.4 0-68.8 28.8-68.8 67.2v731.2c0 38.4 30.4 67.2 68.8 67.2h820.8c38.4 0 68.8-28.8 70.4-67.2V201.6c0-38.4-30.4-67.2-68.8-67.2zM105.6 932.8V355.2h820.8s0 577.6 1.6 577.6H105.6z\" fill=\"#707070\" p-id=\"4967\"></path><path d=\"M500.8 548.8l-49.6 33.6c14.4 16 33.6 41.6 60.8 75.2l54.4-35.2c-19.2-22.4-40-46.4-65.6-73.6z\" fill=\"#707070\" p-id=\"4968\"></path><path d=\"M553.6 451.2l14.4-14.4v-1.6H480c-51.2 68.8-118.4 121.6-196.8 155.2 11.2 12.8 25.6 28.8 41.6 54.4 80-40 142.4-89.6 188.8-148.8 43.2 59.2 102.4 107.2 180.8 144 14.4-19.2 27.2-35.2 41.6-52.8-76.8-30.4-137.6-76.8-182.4-136zM339.2 716.8h246.4c-30.4 43.2-62.4 81.6-94.4 116.8l60.8 33.6c49.6-56 89.6-108.8 123.2-155.2v-54.4h-336v59.2z\" fill=\"#707070\" p-id=\"4969\"></path></svg>\r\n </div>\r\n </template>\r\n </div>\r\n <div v-if=\"hourHeaders && hourHeaders.length > 0\" class=\"header\">\r\n <template v-for='item in hourHeaders' :key=\"item.title\">\r\n <div class=\"headerCaption\" :style=\"{ width: item.width + 'px',marginTop: '-2px' }\"><span :style=\"{ width: item.width + 'px' }\">{{item.title}}</span></div>\r\n </template>\r\n </div>\r\n </div>\r\n </template>\r\n <script lang=\"ts\">\r\n import { defineComponent, toRefs } from 'vue';\r\n import dayjs from 'dayjs';\r\n import { useI18n } from './i18n';\r\n\r\n export default defineComponent({\r\n props: {\r\n weekHeaders: {\r\n type: Array as () => Array<{ title: string; width: number }>,\r\n default: () => []\r\n },\r\n dayHeaders: {\r\n type: Array as () => Array<{ title: string; width: number; fulldate: string }>,\r\n default: () => []\r\n },\r\n monthHeaders: {\r\n type: Array as () => Array<{ title: string; width: number }>,\r\n default: () => []\r\n },\r\n hourHeaders: {\r\n type: Array as () => Array<{ title: string; width: number }>,\r\n default: () => []\r\n }\r\n },\r\n setup(props) {\r\n const { weekHeaders, dayHeaders, monthHeaders, hourHeaders } = toRefs(props);\r\n const { locale } = useI18n();\r\n\r\n const isToday = (title: string) => {\r\n const localeMap: Record<string, string> = {\r\n 'zh-CN': 'zh-cn',\r\n 'en-US': 'en',\r\n 'ja-JP': 'ja',\r\n 'ko-KR': 'ko',\r\n 'fr-FR': 'fr',\r\n 'de-DE': 'de',\r\n 'es-ES': 'es',\r\n 'ru-RU': 'ru'\r\n };\r\n const dayjsLocale = localeMap[locale.value] || 'en';\r\n return title === dayjs().locale(dayjsLocale).format('YYYY-MM-DD');\r\n };\r\n\r\n return {\r\n weekHeaders,\r\n dayHeaders,\r\n monthHeaders,\r\n hourHeaders,\r\n isToday\r\n };\r\n }\r\n });\r\n </script>\r\n <style lang=\"scss\" scoped>\r\n .headerContainer {\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: flex-start;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n font-size: 12px;\r\n font-weight: 600;\r\n font-family: var(--font-family, 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif);\r\n }\r\n\r\n .header {\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n flex-flow: row nowrap;\r\n justify-content: flex-start;\r\n border-bottom: 1px solid var(--border, #cecece);\r\n }\r\n\r\n .headerCaption {\r\n text-align: center;\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: center;\r\n color: var(--text-primary, #333333);\r\n font-size: 12px;\r\n font-weight: 600;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n position: relative;\r\n letter-spacing: 0.5px;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n \r\n &::before {\r\n content: '';\r\n position: absolute;\r\n top: 0;\r\n right: 0;\r\n bottom: 0;\r\n left: 0;\r\n border-top: 1px solid var(--border, #cecece);\r\n border-right: 1px solid var(--border, #cecece);\r\n pointer-events: none;\r\n }\r\n\r\n &:hover {\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n color: var(--primary, #0078d4);\r\n }\r\n}\r\n\r\n .today {\r\n position: absolute;\r\n z-index: 10;\r\n top: 2px;\r\n left: 2px;\r\n height: 20px;\r\n width: 25px;\r\n fill: var(--text-secondary, #707070);\r\n transition: fill var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n .today:hover {\r\n fill: var(--primary, #3A8EE6);\r\n }\r\n </style>","<template>\r\n <div class=\"headerContainer\">\r\n <div v-if=\"monthHeaders && monthHeaders.length > 0\" class=\"header\">\r\n <template v-for='item in monthHeaders' :key=\"item.title\">\r\n <div class=\"headerCaption\" style=\"border-bottom:0px\" :style=\"{ width: item.width + 'px' }\"><span :style=\"{ width: item.width + 'px' }\">{{item.title}}</span></div>\r\n </template>\r\n </div>\r\n <div v-if=\"weekHeaders && weekHeaders.length > 0\" class=\"header\">\r\n <template v-for='item in weekHeaders' :key=\"item.title\">\r\n <div class=\"headerCaption\" style=\"border-top:1px\" :style=\"{ width: item.width + 'px' }\"><span :style=\"{ width: item.width + 'px' }\">{{item.title}}</span>\r\n </div>\r\n </template>\r\n </div>\r\n <div v-if=\"dayHeaders && dayHeaders.length > 0\" class=\"header\">\r\n <!-- 移除未使用的索引变量 -->\r\n <template v-for='item in dayHeaders' :key=\"item.title\">\r\n <div class=\"headerCaption\" :style=\"{ width: item.width + 'px' }\"><span :style=\"{ width: item.width + 'px' }\">{{item.title}}</span>\r\n <svg t=\"1647262391689\" v-if=\"isToday(item.fulldate)\" class=\"today\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"4965\" width=\"200\" height=\"200\"><path d=\"M753.6 222.4h24c19.2 0 33.6-14.4 33.6-32V57.6c0-19.2-14.4-33.6-33.6-33.6h-24c-19.2 0-33.6 14.4-33.6 33.6v131.2c0 19.2 14.4 33.6 33.6 33.6zM251.2 222.4h24c19.2 0 33.6-14.4 33.6-32V57.6c0-19.2-14.4-33.6-33.6-33.6h-24c-19.2 0-33.6 14.4-33.6 33.6v131.2c0 19.2 14.4 33.6 33.6 33.6z\" fill=\"#707070\" p-id=\"4966\"></path><path d=\"M928 134.4h-68.8v56c0 41.6-33.6 76.8-80 76.8h-24c-43.2 0-80-33.6-80-76.8V134.4h-320v56c0 41.6-33.6 76.8-80 76.8h-24c-43.2 0-80-33.6-80-76.8V134.4H105.6c-38.4 0-68.8 28.8-68.8 67.2v731.2c0 38.4 30.4 67.2 68.8 67.2h820.8c38.4 0 68.8-28.8 70.4-67.2V201.6c0-38.4-30.4-67.2-68.8-67.2zM105.6 932.8V355.2h820.8s0 577.6 1.6 577.6H105.6z\" fill=\"#707070\" p-id=\"4967\"></path><path d=\"M500.8 548.8l-49.6 33.6c14.4 16 33.6 41.6 60.8 75.2l54.4-35.2c-19.2-22.4-40-46.4-65.6-73.6z\" fill=\"#707070\" p-id=\"4968\"></path><path d=\"M553.6 451.2l14.4-14.4v-1.6H480c-51.2 68.8-118.4 121.6-196.8 155.2 11.2 12.8 25.6 28.8 41.6 54.4 80-40 142.4-89.6 188.8-148.8 43.2 59.2 102.4 107.2 180.8 144 14.4-19.2 27.2-35.2 41.6-52.8-76.8-30.4-137.6-76.8-182.4-136zM339.2 716.8h246.4c-30.4 43.2-62.4 81.6-94.4 116.8l60.8 33.6c49.6-56 89.6-108.8 123.2-155.2v-54.4h-336v59.2z\" fill=\"#707070\" p-id=\"4969\"></path></svg>\r\n </div>\r\n </template>\r\n </div>\r\n <div v-if=\"hourHeaders && hourHeaders.length > 0\" class=\"header\">\r\n <template v-for='item in hourHeaders' :key=\"item.title\">\r\n <div class=\"headerCaption\" :style=\"{ width: item.width + 'px',marginTop: '-2px' }\"><span :style=\"{ width: item.width + 'px' }\">{{item.title}}</span></div>\r\n </template>\r\n </div>\r\n </div>\r\n </template>\r\n <script lang=\"ts\">\r\n import { defineComponent, toRefs } from 'vue';\r\n import dayjs from 'dayjs';\r\n import { useI18n } from './i18n';\r\n\r\n export default defineComponent({\r\n props: {\r\n weekHeaders: {\r\n type: Array as () => Array<{ title: string; width: number }>,\r\n default: () => []\r\n },\r\n dayHeaders: {\r\n type: Array as () => Array<{ title: string; width: number; fulldate: string }>,\r\n default: () => []\r\n },\r\n monthHeaders: {\r\n type: Array as () => Array<{ title: string; width: number }>,\r\n default: () => []\r\n },\r\n hourHeaders: {\r\n type: Array as () => Array<{ title: string; width: number }>,\r\n default: () => []\r\n }\r\n },\r\n setup(props) {\r\n const { weekHeaders, dayHeaders, monthHeaders, hourHeaders } = toRefs(props);\r\n const { locale } = useI18n();\r\n\r\n const isToday = (title: string) => {\r\n const localeMap: Record<string, string> = {\r\n 'zh-CN': 'zh-cn',\r\n 'en-US': 'en',\r\n 'ja-JP': 'ja',\r\n 'ko-KR': 'ko',\r\n 'fr-FR': 'fr',\r\n 'de-DE': 'de',\r\n 'es-ES': 'es',\r\n 'ru-RU': 'ru'\r\n };\r\n const dayjsLocale = localeMap[locale.value] || 'en';\r\n return title === dayjs().locale(dayjsLocale).format('YYYY-MM-DD');\r\n };\r\n\r\n return {\r\n weekHeaders,\r\n dayHeaders,\r\n monthHeaders,\r\n hourHeaders,\r\n isToday\r\n };\r\n }\r\n });\r\n </script>\r\n <style lang=\"scss\" scoped>\r\n .headerContainer {\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: flex-start;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n font-size: 12px;\r\n font-weight: 600;\r\n font-family: var(--font-family, 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif);\r\n }\r\n\r\n .header {\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n flex-flow: row nowrap;\r\n justify-content: flex-start;\r\n border-bottom: 1px solid var(--border, #cecece);\r\n }\r\n\r\n .headerCaption {\r\n text-align: center;\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: center;\r\n color: var(--text-primary, #333333);\r\n font-size: 12px;\r\n font-weight: 600;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n position: relative;\r\n letter-spacing: 0.5px;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n \r\n &::before {\r\n content: '';\r\n position: absolute;\r\n top: 0;\r\n right: 0;\r\n bottom: 0;\r\n left: 0;\r\n border-top: 1px solid var(--border, #cecece);\r\n border-right: 1px solid var(--border, #cecece);\r\n pointer-events: none;\r\n }\r\n\r\n &:hover {\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n color: var(--primary, #0078d4);\r\n }\r\n}\r\n\r\n .today {\r\n position: absolute;\r\n z-index: 10;\r\n top: 2px;\r\n left: 2px;\r\n height: 20px;\r\n width: 25px;\r\n fill: var(--text-secondary, #707070);\r\n transition: fill var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n .today:hover {\r\n fill: var(--primary, #3A8EE6);\r\n }\r\n </style>","!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define(t):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).dayjs_plugin_isoWeek=t()}(this,(function(){\"use strict\";var e=\"day\";return function(t,i,s){var a=function(t){return t.add(4-t.isoWeekday(),e)},d=i.prototype;d.isoWeekYear=function(){return a(this).year()},d.isoWeek=function(t){if(!this.$utils().u(t))return this.add(7*(t-this.isoWeek()),e);var i,d,n,o,r=a(this),u=(i=this.isoWeekYear(),d=this.$u,n=(d?s.utc:s)().year(i).startOf(\"year\"),o=4-n.isoWeekday(),n.isoWeekday()>4&&(o+=7),n.add(o,e));return r.diff(u,\"week\")+1},d.isoWeekday=function(e){return this.$utils().u(e)?this.day()||7:this.day(this.day()%7?e:e-7)};var n=d.startOf;d.startOf=function(e,t){var i=this.$utils(),s=!!i.u(t)||t;return\"isoweek\"===i.p(e)?s?this.date(this.date()-(this.isoWeekday()-1)).startOf(\"day\"):this.date(this.date()-1-(this.isoWeekday()-1)+7).endOf(\"day\"):n.bind(this)(e,t)}}}));","<template>\r\n <div v-if='showRow' class=\"barRow\" :style=\"{ height: rowHeight + 'px' }\" @mouseover=\"hoverActive()\"\r\n @mouseleave=\"hoverInactive()\" :class=\"{ active: hover }\" :data-task-id=\"row[mapFields.id]\">\r\n <svg key=\"row.no\" v-if='showRow' ref='bar' class=\"bar\" :height=\"barHeight + 'px'\"\r\n :class=\"{ active: hover }\" style=\"overflow: visible;\"></svg>\r\n <template v-for='(count) in timelineCellCount'\r\n :key=\"count + row.id + timelineCellCount + showRow + '_template'\">\r\n <div class=\"cell\"\r\n :style=\"{ width: scale + 'px', minWidth: scale + 'px', maxWidth: scale + 'px', height: rowHeight + 'px', background: WeekEndColor(count) }\">\r\n </div>\r\n </template>\r\n </div>\r\n</template>\r\n<script lang=\"ts\">\r\nimport { defineComponent, watch, ref, computed, onMounted, onDeactivated, onBeforeUnmount, inject } from 'vue';\r\nimport SVG from 'svg.js';\r\nimport interact from 'interactjs';\r\nimport dayjs from 'dayjs';\r\nimport isoWeek from 'dayjs/plugin/isoWeek';\r\ndayjs.extend(isoWeek);\r\nimport { store, mutations } from './Store';\r\nimport sharedState from '../gantt/ShareState';\r\nimport { Symbols } from './Symbols';\r\n\r\nexport default defineComponent({\r\n name: 'Bar',\r\n emits: ['progress-update'],\r\n props: {\r\n rowHeight: { type: Number as () => number, default: 0 },\r\n row: { type: Object as () => Record<string, any>, default: () => ({}) },\r\n startGanttDate: { type: String as () => string },\r\n endGanttDate: { type: String as () => string }\r\n },\r\n setup(props, { emit }) {\r\n const bar = ref<SVGSVGElement | null>(null);\r\n const barHeight = ref(props.rowHeight * 0.7);\r\n const direction = ref<string | null>(null);\r\n const oldBarDataX = ref(0);\r\n const oldBarWidth = ref(0);\r\n const showRow = ref(true);\r\n const hover = ref(false);\r\n const barColor = ref('');\r\n const isBarInteracted = ref(false);\r\n const themeVersion = ref(0);\r\n const isProgressDragging = ref(false);\r\n\r\n const timelineCellCount = computed(() => store.timelineCellCount);\r\n const scale = computed(() => store.scale);\r\n const mode = computed(() => store.mode);\r\n const mapFields = computed(() => store.mapFields);\r\n const progress = computed(() => {\r\n const progressValue = Number(props.row[mapFields.value.progress]);\r\n if (isNaN(progressValue)) return '0.00%';\r\n return (progressValue * 100).toFixed(2) + '%';\r\n });\r\n\r\n const setBarColor = inject(Symbols.SetBarColorSymbol) as ((row: any) => string) | undefined;\r\n\r\n // 进度更新事件\r\n const emitProgressUpdate = (newProgress: number) => {\r\n const detail = {\r\n taskId: props.row[mapFields.value.id],\r\n oldProgress: Number(props.row[mapFields.value.progress]) || 0,\r\n newProgress: newProgress,\r\n task: props.row\r\n };\r\n emit('progress-update', detail);\r\n window.dispatchEvent(new CustomEvent('taskProgressUpdate', { detail }));\r\n console.log('Task progress updated:', detail);\r\n };\r\n\r\n watch(() => sharedState.highlightedId, (newId) => {\r\n hover.value = props.row[mapFields.value['id']] === newId;\r\n });\r\n\r\n const hoverActive = () => sharedState.triggerHighlight(props.row[mapFields.value.id] as number | null);\r\n const hoverInactive = () => sharedState.triggerHighlight(null);\r\n\r\n const WeekEndColor = (count: number) => {\r\n themeVersion.value;\r\n let bgContent = '#ffffff', bgSecondary = '#f8f8f8';\r\n if (bar.value) {\r\n let element = bar.value.parentElement;\r\n while (element) {\r\n if (element.hasAttribute('data-gantt-theme')) {\r\n bgContent = getComputedStyle(element).getPropertyValue('--bg-content').trim() || '#ffffff';\r\n bgSecondary = getComputedStyle(element).getPropertyValue('--bg-secondary').trim() || '#f8f8f8';\r\n break;\r\n }\r\n element = element.parentElement;\r\n }\r\n }\r\n switch (mode.value) {\r\n case '月': case '日': {\r\n let currentDate = dayjs(props.startGanttDate).add(count, 'days');\r\n return (currentDate.isoWeekday() === 7 || currentDate.isoWeekday() === 1) ? bgSecondary : bgContent;\r\n }\r\n case '周': case '时': return bgContent;\r\n }\r\n };\r\n\r\n const setBarDate = mutations.setBarDate;\r\n const setAllowChangeTaskDate = mutations.setAllowChangeTaskDate;\r\n\r\n const drowBar = (barElement: SVGSVGElement) => {\r\n let dataX = 0;\r\n switch (mode.value) {\r\n case '月': case '日': {\r\n let fromPlanStartDays = dayjs(props.row[mapFields.value.startdate]).diff(dayjs(props.startGanttDate), 'days');\r\n dataX = scale.value * fromPlanStartDays;\r\n let spendDays = dayjs(props.row[mapFields.value.enddate]).diff(dayjs(props.row[mapFields.value.startdate]), 'days') + 1;\r\n oldBarWidth.value = spendDays * scale.value;\r\n props.row[mapFields.value.takestime] = spendDays + '天';\r\n break;\r\n }\r\n case '周': {\r\n const startGanttWeek = dayjs(props.startGanttDate).startOf('isoWeek');\r\n const taskStartWeek = dayjs(props.row[mapFields.value.startdate]).startOf('isoWeek');\r\n let fromPlanStartWeeks = taskStartWeek.diff(startGanttWeek, 'week');\r\n dataX = scale.value * fromPlanStartWeeks;\r\n const taskEndWeek = dayjs(props.row[mapFields.value.enddate]).startOf('isoWeek');\r\n let spendWeeks = taskEndWeek.diff(taskStartWeek, 'week') + 1;\r\n oldBarWidth.value = spendWeeks * scale.value;\r\n props.row[mapFields.value.takestime] = spendWeeks + '周';\r\n break;\r\n }\r\n case '时': {\r\n let fromPlanStartHours = dayjs(props.row[mapFields.value.startdate]).diff(dayjs(props.startGanttDate), 'hours');\r\n dataX = scale.value * fromPlanStartHours;\r\n let spendHours = dayjs(props.row[mapFields.value.enddate]).diff(dayjs(props.row[mapFields.value.startdate]), 'hours') + 1;\r\n oldBarWidth.value = spendHours * scale.value;\r\n props.row[mapFields.value.takestime] = spendHours + '小时';\r\n break;\r\n }\r\n }\r\n oldBarDataX.value = dataX;\r\n let svg = SVG(barElement as unknown as HTMLElement);\r\n const borderColor = getComputedStyle(barElement).getPropertyValue('--border') || '#cecece';\r\n barElement.setAttribute('data-x', dataX.toString());\r\n barElement.setAttribute('width', oldBarWidth.value.toString());\r\n barElement.setAttribute('stroke', borderColor);\r\n barElement.setAttribute('stroke-width', '1px');\r\n barElement.style.transform = `translate(${dataX}px, 0px)`;\r\n\r\n let p = svg.select('pattern').first();\r\n let g = (svg.children().filter((child) => child.type === 'g')[0] as any) || svg.group();\r\n let innerRect = svg.select('.innerRect').first();\r\n let outerRect = svg.select('rect:not(.innerRect):not(.progressHandle)').first();\r\n let text = svg.select('text').first();\r\n let progressHandle = svg.select('.progressHandle').first();\r\n\r\n if (!p) {\r\n p = svg.pattern(10, 10, (add) => {\r\n (add as any).path('M10 -5 -10,15M15,0,0,15M0 -5 -20,15').fill('none').stroke({ color: 'gray', opacity: 0.4, width: 5 });\r\n });\r\n }\r\n if (!g) g = svg.group();\r\n\r\n let innerRectWidth = props.row[mapFields.value.progress] \r\n ? Number(oldBarWidth.value) * Number(props.row[mapFields.value.progress]) \r\n : Number(oldBarWidth.value);\r\n \r\n if (!innerRect) {\r\n innerRect = svg.rect(innerRectWidth, barHeight.value).radius(10);\r\n innerRect.addClass('innerRect');\r\n g.add(innerRect);\r\n } else {\r\n innerRect.fill({ color: barColor.value, opacity: 0.4 });\r\n innerRect.width(innerRectWidth);\r\n }\r\n\r\n if (!outerRect) {\r\n outerRect = svg.rect(oldBarWidth.value, barHeight.value).radius(10).fill(p).stroke({ color: borderColor, width: 1 });\r\n outerRect.on('mouseover', () => outerRect.animate(200).attr({ stroke: '#000', strokeWidth: 2, opacity: 1 }));\r\n outerRect.on('mouseleave', () => outerRect.animate(200).attr({ stroke: '#0066ff', strokeWidth: 10, opacity: 0.4 }));\r\n } else {\r\n outerRect.width(oldBarWidth.value);\r\n }\r\n\r\n if (!text) {\r\n text = svg.text(progress.value).stroke('#faf7ec');\r\n } else {\r\n (text as any).text(progress.value);\r\n }\r\n const textBBox = text.bbox();\r\n (text as any).font({ size: 15, anchor: 'middle', leading: '1em' })\r\n .fill('#000').attr('opacity', 1).attr('dominant-baseline', 'middle')\r\n .center(innerRect.width() / 2 + textBBox.width / 2, innerRect.height() / 2);\r\n\r\n // 进度调节滑块 - 三角形拖拽手柄 + 对齐虚线\r\n const handleWidth = 12;\r\n const handleHeight = 8;\r\n const handleX = innerRectWidth - handleWidth / 2;\r\n // 三角形位置:Bar底部边缘穿过三角形中心\r\n const handleY = barHeight.value - handleHeight / 2;\r\n const lineX = innerRectWidth;\r\n \r\n // 获取主题颜色的辅助函数\r\n const getThemeColors = () => {\r\n let primary = '#f59e0b', primaryDark = '#d97706', primaryLight = '#fbbf24';\r\n let element = barElement.parentElement;\r\n while (element) {\r\n if (element.hasAttribute('data-gantt-theme')) {\r\n primary = getComputedStyle(element).getPropertyValue('--primary').trim() || primary;\r\n primaryDark = getComputedStyle(element).getPropertyValue('--primary-dark').trim() || primaryDark;\r\n primaryLight = getComputedStyle(element).getPropertyValue('--primary-light').trim() || primaryLight;\r\n break;\r\n }\r\n element = element.parentElement;\r\n }\r\n return { primary, primaryDark, primaryLight };\r\n };\r\n \r\n // 查找或创建对齐虚线\r\n let guideLineEl = barElement.querySelector('.progressGuideLine') as SVGLineElement | null;\r\n const themeColors = getThemeColors();\r\n \r\n if (!guideLineEl) {\r\n // 创建对齐虚线\r\n const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');\r\n line.setAttribute('class', 'progressGuideLine');\r\n line.setAttribute('x1', String(lineX));\r\n line.setAttribute('y1', '0');\r\n line.setAttribute('x2', String(lineX));\r\n line.setAttribute('y2', String(handleY));\r\n line.setAttribute('stroke', themeColors.primaryDark);\r\n line.setAttribute('stroke-width', '2');\r\n line.setAttribute('stroke-dasharray', '4,3');\r\n line.setAttribute('opacity', '0.8');\r\n barElement.appendChild(line);\r\n guideLineEl = line;\r\n }\r\n // 始终更新位置和可见性\r\n guideLineEl.setAttribute('x1', String(lineX));\r\n guideLineEl.setAttribute('x2', String(lineX));\r\n guideLineEl.setAttribute('y2', String(handleY));\r\n guideLineEl.setAttribute('stroke', themeColors.primaryDark);\r\n guideLineEl.setAttribute('opacity', '0.8');\r\n \r\n if (!progressHandle) {\r\n // 创建三角形手柄\r\n const trianglePoints = `${handleWidth/2},0 0,${handleHeight} ${handleWidth},${handleHeight}`;\r\n progressHandle = svg.polygon(trianglePoints)\r\n .fill(themeColors.primary)\r\n .stroke({ color: themeColors.primaryDark, width: 1 })\r\n .addClass('progressHandle');\r\n \r\n const handleElement = progressHandle.node as unknown as SVGPolygonElement;\r\n handleElement.style.cursor = 'ew-resize';\r\n handleElement.style.pointerEvents = 'auto';\r\n progressHandle.move(handleX, handleY);\r\n \r\n // 用于记录当前X位置\r\n let currentHandleX = handleX;\r\n \r\n // 悬停效果 - 高亮显示\r\n handleElement.addEventListener('mouseenter', () => {\r\n if (!isProgressDragging.value) {\r\n const colors = getThemeColors();\r\n handleElement.setAttribute('fill', colors.primaryLight);\r\n handleElement.setAttribute('stroke', colors.primary);\r\n handleElement.setAttribute('stroke-width', '2');\r\n guideLineEl!.setAttribute('stroke', colors.primaryLight);\r\n guideLineEl!.setAttribute('stroke-width', '2');\r\n guideLineEl!.setAttribute('opacity', '1');\r\n }\r\n });\r\n handleElement.addEventListener('mouseleave', () => {\r\n if (!isProgressDragging.value) {\r\n const colors = getThemeColors();\r\n handleElement.setAttribute('fill', colors.primary);\r\n handleElement.setAttribute('stroke', colors.primaryDark);\r\n handleElement.setAttribute('stroke-width', '1');\r\n guideLineEl!.setAttribute('stroke', colors.primaryDark);\r\n guideLineEl!.setAttribute('stroke-width', '2');\r\n guideLineEl!.setAttribute('opacity', '0.8');\r\n }\r\n });\r\n \r\n // 拖拽交互\r\n interact(handleElement).draggable({\r\n inertia: false,\r\n listeners: {\r\n start: () => { \r\n isProgressDragging.value = true;\r\n const colors = getThemeColors();\r\n handleElement.setAttribute('fill', colors.primaryDark);\r\n handleElement.setAttribute('stroke', colors.primaryDark);\r\n handleElement.setAttribute('stroke-width', '2');\r\n guideLineEl!.setAttribute('stroke', colors.primaryDark);\r\n guideLineEl!.setAttribute('stroke-width', '2');\r\n guideLineEl!.setAttribute('opacity', '1');\r\n },\r\n move: (event) => {\r\n currentHandleX += event.dx;\r\n currentHandleX = Math.max(-handleWidth / 2, Math.min(currentHandleX, oldBarWidth.value - handleWidth / 2));\r\n \r\n progressHandle.move(currentHandleX, handleY);\r\n \r\n // 更新虚线位置\r\n const newLineX = currentHandleX + handleWidth / 2;\r\n guideLineEl!.setAttribute('x1', String(newLineX));\r\n guideLineEl!.setAttribute('x2', String(newLineX));\r\n \r\n // 更新进度\r\n const newProgress = Math.min(1, Math.max(0, (currentHandleX + handleWidth / 2) / oldBarWidth.value));\r\n innerRect.width(newProgress * oldBarWidth.value);\r\n (text as any).text((newProgress * 100).toFixed(2) + '%');\r\n (text as any).center(innerRect.width() / 2 + textBBox.width / 2, innerRect.height() / 2);\r\n },\r\n end: () => {\r\n isProgressDragging.value = false;\r\n const colors = getThemeColors();\r\n handleElement.setAttribute('fill', colors.primary);\r\n handleElement.setAttribute('stroke', colors.primaryDark);\r\n handleElement.setAttribute('stroke-width', '1');\r\n guideLineEl!.setAttribute('stroke', colors.primaryDark);\r\n guideLineEl!.setAttribute('stroke-width', '2');\r\n guideLineEl!.setAttribute('opacity', '0.8');\r\n \r\n const newProgress = Math.min(1, Math.max(0, (currentHandleX + handleWidth / 2) / oldBarWidth.value));\r\n props.row[mapFields.value.progress] = newProgress;\r\n emitProgressUpdate(newProgress);\r\n }\r\n }\r\n });\r\n } else {\r\n progressHandle.move(handleX, handleY);\r\n // 更新已存在的三角形颜色以响应主题变化\r\n const handleElement = progressHandle.node as unknown as SVGPolygonElement;\r\n handleElement.setAttribute('fill', themeColors.primary);\r\n handleElement.setAttribute('stroke', themeColors.primaryDark);\r\n }\r\n\r\n setBarDate({ id: props.row[mapFields.value.id], startDate: props.row[mapFields.value.startdate], endDate: props.row[mapFields.value.enddate] });\r\n\r\n // 拖拽功能\r\n interact(barElement).draggable({\r\n inertia: false,\r\n modifiers: [interact.modifiers.restrictRect({ restriction: 'parent', endOnly: true })],\r\n autoScroll: true,\r\n listeners: {\r\n start: (event: { target: SVGSVGElement }) => {\r\n if (isProgressDragging.value) return;\r\n oldBarDataX.value = Number(event.target.getAttribute('data-x'));\r\n oldBarWidth.value = event.target.width.baseVal.value;\r\n },\r\n move: (event: { target: SVGSVGElement; dx: number; rect: { width: number; height: number } }) => {\r\n if (isProgressDragging.value) return;\r\n let x = ((parseFloat(event.target.getAttribute('data-x') || '0') || 0) + event.dx).toString();\r\n Object.assign(event.target.style, { width: `${event.rect.width}px`, height: `${event.rect.height}px`, transform: `translate(${x}px, 0px)` });\r\n event.target.setAttribute('data-x', x);\r\n event.target.setAttribute('data-y', '0');\r\n },\r\n end: (event: { target: SVGSVGElement }) => {\r\n if (isProgressDragging.value) return;\r\n let target = event.target;\r\n let currentX = parseFloat(target.getAttribute('data-x') || '0') || 0;\r\n let multiple = Math.round(currentX / scale.value);\r\n let alignedX = multiple * scale.value;\r\n if (alignedX < 0) alignedX = 0;\r\n if (alignedX > timelineCellCount.value * scale.value) alignedX = timelineCellCount.value * scale.value;\r\n \r\n const parentIdField = mapFields.value.parentId || 'pid';\r\n const currentParentId = props.row[parentIdField];\r\n if (currentParentId && currentParentId !== '0') {\r\n const parentTask = store.tasks.find(t => String(t[mapFields.value.id]) === String(currentParentId));\r\n if (parentTask) {\r\n let parentStartX = 0;\r\n if (mode.value === '月' || mode.value === '日') {\r\n parentStartX = dayjs(parentTask[mapFields.value.startdate]).diff(dayjs(props.startGanttDate), 'days') * scale.value;\r\n } else if (mode.value === '周') {\r\n const ganttStartWeek = dayjs(props.startGanttDate).startOf('isoWeek');\r\n const parentStartWeek = dayjs(parentTask[mapFields.value.startdate]).startOf('isoWeek');\r\n parentStartX = parentStartWeek.diff(ganttStartWeek, 'week') * scale.value;\r\n } else if (mode.value === '时') {\r\n parentStartX = dayjs(parentTask[mapFields.value.startdate]).diff(dayjs(props.startGanttDate), 'hours') * scale.value;\r\n }\r\n if (alignedX < parentStartX) alignedX = parentStartX;\r\n }\r\n }\r\n \r\n target.style.transform = `translate(${alignedX}px, 0px)`;\r\n target.setAttribute('data-x', alignedX.toString());\r\n \r\n const cellsMoved = Math.round((alignedX - oldBarDataX.value) / scale.value);\r\n let daysOffset = 0, hoursOffset = 0;\r\n if (mode.value === '月' || mode.value === '日') daysOffset = cellsMoved;\r\n else if (mode.value === '周') daysOffset = cellsMoved * 7;\r\n else if (mode.value === '时') hoursOffset = cellsMoved;\r\n \r\n if (mode.value === '时') {\r\n props.row[mapFields.value.startdate] = dayjs(props.row[mapFields.value.startdate]).add(hoursOffset, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n props.row[mapFields.value.enddate] = dayjs(props.row[mapFields.value.enddate]).add(hoursOffset, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n props.row[mapFields.value.takestime] = (dayjs(props.row[mapFields.value.enddate]).diff(dayjs(props.row[mapFields.value.startdate]), 'hours') + 1) + '小时';\r\n } else {\r\n props.row[mapFields.value.startdate] = dayjs(props.row[mapFields.value.startdate]).add(daysOffset, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n props.row[mapFields.value.enddate] = dayjs(props.row[mapFields.value.enddate]).add(daysOffset, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n if (mode.value === '月' || mode.value === '日') {\r\n props.row[mapFields.value.takestime] = (dayjs(props.row[mapFields.value.enddate]).diff(dayjs(props.row[mapFields.value.startdate]), 'days') + 1) + '天';\r\n } else if (mode.value === '周') {\r\n const startWeek = dayjs(props.row[mapFields.value.startdate]).startOf('isoWeek');\r\n const endWeek = dayjs(props.row[mapFields.value.enddate]).startOf('isoWeek');\r\n props.row[mapFields.value.takestime] = (endWeek.diff(startWeek, 'week') + 1) + '周';\r\n }\r\n }\r\n \r\n // 联动子任务\r\n const newParentStartDate = dayjs(props.row[mapFields.value.startdate]);\r\n const currentTaskId = props.row[mapFields.value.id];\r\n const getAllChildren = (parentId: any, tasks: any[]): any[] => {\r\n const children: any[] = [];\r\n for (const task of tasks) {\r\n if (String(task[parentIdField]) === String(parentId)) {\r\n children.push(task);\r\n children.push(...getAllChildren(task[mapFields.value.id], tasks));\r\n }\r\n }\r\n return children;\r\n };\r\n const childTasks = getAllChildren(currentTaskId, store.tasks);\r\n const actualOffset = mode.value === '时' ? hoursOffset : daysOffset;\r\n if (actualOffset > 0) {\r\n for (const child of childTasks) {\r\n if (dayjs(child[mapFields.value.startdate]).isBefore(newParentStartDate)) {\r\n if (mode.value === '时') {\r\n child[mapFields.value.startdate] = dayjs(child[mapFields.value.startdate]).add(hoursOffset, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n child[mapFields.value.enddate] = dayjs(child[mapFields.value.enddate]).add(hoursOffset, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n } else {\r\n child[mapFields.value.startdate] = dayjs(child[mapFields.value.startdate]).add(daysOffset, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n child[mapFields.value.enddate] = dayjs(child[mapFields.value.enddate]).add(daysOffset, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n }\r\n setBarDate({ id: child[mapFields.value.id], startDate: child[mapFields.value.startdate], endDate: child[mapFields.value.enddate] });\r\n }\r\n }\r\n }\r\n setBarDate({ id: props.row[mapFields.value.id], startDate: props.row[mapFields.value.startdate], endDate: props.row[mapFields.value.enddate] });\r\n }\r\n }\r\n });\r\n\r\n // 调整大小功能\r\n interact(barElement).resizable({\r\n edges: { left: true, right: true, bottom: false, top: false },\r\n listeners: {\r\n start: (event: { target: SVGSVGElement }) => {\r\n oldBarDataX.value = Number(event.target.getAttribute('data-x'));\r\n oldBarWidth.value = Number(event.target.getAttribute('width'));\r\n },\r\n end: (event: { target: SVGSVGElement; rect: { width: number }; edges: { left: boolean; right: boolean } }) => {\r\n setAllowChangeTaskDate(props.row);\r\n let target = event.target;\r\n let newWidth = event.rect.width;\r\n let widthCells = Math.round(newWidth / scale.value);\r\n if (widthCells < 1) widthCells = 1;\r\n newWidth = widthCells * scale.value;\r\n let currentX = oldBarDataX.value;\r\n \r\n if (event.edges && event.edges.left) {\r\n currentX = oldBarDataX.value + (oldBarWidth.value - newWidth);\r\n currentX = Math.round(currentX / scale.value) * scale.value;\r\n if (currentX < 0) currentX = 0;\r\n \r\n const parentIdField = mapFields.value.parentId || 'pid';\r\n const currentParentId = props.row[parentIdField];\r\n if (currentParentId && currentParentId !== '0') {\r\n const parentTask = store.tasks.find(t => String(t[mapFields.value.id]) === String(currentParentId));\r\n if (parentTask) {\r\n let parentStartX = 0;\r\n if (mode.value === '月' || mode.value === '日') {\r\n parentStartX = dayjs(parentTask[mapFields.value.startdate]).diff(dayjs(props.startGanttDate), 'days') * scale.value;\r\n } else if (mode.value === '周') {\r\n const ganttStartWeek = dayjs(props.startGanttDate).startOf('isoWeek');\r\n const parentStartWeek = dayjs(parentTask[mapFields.value.startdate]).startOf('isoWeek');\r\n parentStartX = parentStartWeek.diff(ganttStartWeek, 'week') * scale.value;\r\n } else if (mode.value === '时') {\r\n parentStartX = dayjs(parentTask[mapFields.value.startdate]).diff(dayjs(props.startGanttDate), 'hours') * scale.value;\r\n }\r\n if (currentX < parentStartX) {\r\n currentX = parentStartX;\r\n newWidth = oldBarDataX.value + oldBarWidth.value - currentX;\r\n widthCells = Math.round(newWidth / scale.value);\r\n if (widthCells < 1) widthCells = 1;\r\n newWidth = widthCells * scale.value;\r\n }\r\n }\r\n }\r\n }\r\n \r\n target.setAttribute('width', newWidth.toString());\r\n target.style.width = newWidth + 'px';\r\n target.style.transform = `translate(${currentX}px, 0px)`;\r\n target.setAttribute('data-x', currentX.toString());\r\n \r\n const startCellIndex = Math.round(currentX / scale.value);\r\n const endCellIndex = startCellIndex + widthCells - 1;\r\n let newStartDate: string, newEndDate: string;\r\n \r\n if (mode.value === '月' || mode.value === '日') {\r\n newStartDate = dayjs(props.startGanttDate).add(startCellIndex, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n newEndDate = dayjs(props.startGanttDate).add(endCellIndex, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n props.row[mapFields.value.takestime] = widthCells + '天';\r\n } else if (mode.value === '周') {\r\n const ganttStartWeek = dayjs(props.startGanttDate).startOf('isoWeek');\r\n newStartDate = ganttStartWeek.add(startCellIndex, 'weeks').format('YYYY-MM-DD HH:mm:ss');\r\n newEndDate = ganttStartWeek.add(endCellIndex, 'weeks').endOf('isoWeek').format('YYYY-MM-DD HH:mm:ss');\r\n props.row[mapFields.value.takestime] = widthCells + '周';\r\n } else {\r\n newStartDate = dayjs(props.startGanttDate).add(startCellIndex, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n newEndDate = dayjs(props.startGanttDate).add(endCellIndex, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n props.row[mapFields.value.takestime] = widthCells + '小时';\r\n }\r\n \r\n props.row[mapFields.value.startdate] = newStartDate;\r\n props.row[mapFields.value.enddate] = newEndDate;\r\n \r\n let svg = SVG(barElement as unknown as HTMLElement);\r\n let innerRect = svg.select('.innerRect').first();\r\n let outerRect = svg.select('rect:not(.innerRect):not(.progressHandle)').first();\r\n if (innerRect) {\r\n let innerRectWidth = props.row[mapFields.value.progress] ? newWidth * Number(props.row[mapFields.value.progress]) : newWidth;\r\n innerRect.width(innerRectWidth);\r\n }\r\n if (outerRect) outerRect.width(newWidth);\r\n \r\n // 联动子任务\r\n if (event.edges && event.edges.left) {\r\n const newParentStartDate = dayjs(newStartDate);\r\n const currentTaskId = props.row[mapFields.value.id];\r\n const parentIdField = mapFields.value.parentId || 'pid';\r\n const getAllChildren = (parentId: any, tasks: any[]): any[] => {\r\n const children: any[] = [];\r\n for (const task of tasks) {\r\n if (String(task[parentIdField]) === String(parentId)) {\r\n children.push(task);\r\n children.push(...getAllChildren(task[mapFields.value.id], tasks));\r\n }\r\n }\r\n return children;\r\n };\r\n const childTasks = getAllChildren(currentTaskId, store.tasks);\r\n for (const child of childTasks) {\r\n if (dayjs(child[mapFields.value.startdate]).isBefore(newParentStartDate)) {\r\n const childOffset = newParentStartDate.diff(dayjs(child[mapFields.value.startdate]), mode.value === '时' ? 'hours' : 'days');\r\n if (mode.value === '时') {\r\n child[mapFields.value.startdate] = dayjs(child[mapFields.value.startdate]).add(childOffset, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n child[mapFields.value.enddate] = dayjs(child[mapFields.value.enddate]).add(childOffset, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n } else {\r\n child[mapFields.value.startdate] = dayjs(child[mapFields.value.startdate]).add(childOffset, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n child[mapFields.value.enddate] = dayjs(child[mapFields.value.enddate]).add(childOffset, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n }\r\n setBarDate({ id: child[mapFields.value.id], startDate: child[mapFields.value.startdate], endDate: child[mapFields.value.enddate] });\r\n }\r\n }\r\n }\r\n setBarDate({ id: props.row[mapFields.value.id], startDate: newStartDate, endDate: newEndDate });\r\n }\r\n },\r\n modifiers: [\r\n interact.modifiers.restrictEdges({ outer: 'parent' }),\r\n interact.modifiers.restrictSize({ min: { width: scale.value, height: barHeight.value } })\r\n ],\r\n inertia: false, hold: 1\r\n });\r\n };\r\n\r\n watch(() => [props.row[mapFields.value.startdate], props.row[mapFields.value.enddate]], () => {\r\n if (bar.value && isBarInteracted.value) drowBar(bar.value);\r\n }, { deep: false });\r\n\r\n onMounted(() => {\r\n console.log(`[Bar] Component mounted, task: ${props.row[mapFields.value.id]}, mode: ${store.mode}`);\r\n if (bar.value && !isBarInteracted.value) {\r\n drowBar(bar.value);\r\n isBarInteracted.value = true;\r\n }\r\n if (setBarColor) {\r\n barColor.value = setBarColor(props.row);\r\n if (bar.value) drowBar(bar.value);\r\n }\r\n // 监听主题变化\r\n if (bar.value) {\r\n let ganttContainer = bar.value.parentElement;\r\n while (ganttContainer && !ganttContainer.hasAttribute('data-gantt-theme')) {\r\n ganttContainer = ganttContainer.parentElement;\r\n }\r\n if (ganttContainer) {\r\n const observer = new MutationObserver(() => { themeVersion.value++; });\r\n observer.observe(ganttContainer, { attributes: true, attributeFilter: ['data-gantt-theme', 'style'] });\r\n onBeforeUnmount(() => observer.disconnect());\r\n }\r\n }\r\n });\r\n\r\n onDeactivated(() => {\r\n if (bar.value) { try { interact(bar.value).unset(); } catch (e) {} }\r\n showRow.value = false;\r\n });\r\n\r\n onBeforeUnmount(() => {\r\n if (bar.value) { try { interact(bar.value).unset(); } catch (e) {} }\r\n showRow.value = false;\r\n });\r\n\r\n return {\r\n bar, barHeight, direction, oldBarDataX, oldBarWidth, showRow, hover, barColor,\r\n timelineCellCount, scale, mode, mapFields, hoverActive, hoverInactive, WeekEndColor\r\n };\r\n },\r\n})\r\n</script>\r\n<style lang=\"scss\" scoped>\r\n.barRow.active { background: var(--row-hover, #FFF3A1) !important; .cell { background: var(--row-hover, #FFF3A1) !important; } }\r\n.barRow {\r\n display: flex; flex-flow: row nowrap; align-items: center; justify-content: flex-start;\r\n width: fit-content; position: relative; overflow: visible;\r\n .bar { position: absolute; z-index: 100; background-color: #faf7ec; border-radius: 10px; overflow: visible; }\r\n &:first-child .cell {\r\n border-top: none; display: flex; align-items: center; justify-content: center; font-size: 10px;\r\n position: relative; margin: 0; box-sizing: border-box;\r\n &:first-child { border-left: 1px solid var(--border, #cecece); }\r\n &:not(:last-child) { border-right: 1px solid var(--border, #cecece); }\r\n &:last-child { border-right: 1px solid var(--border, #cecece); }\r\n }\r\n &:not(:first-child) .cell {\r\n border-top: 1px solid var(--border, #cecece); display: flex; align-items: center; justify-content: center;\r\n font-size: 10px; position: relative; margin: 0; box-sizing: border-box;\r\n &:first-child { border-left: 1px solid var(--border, #cecece); }\r\n &:not(:last-child) { border-right: 1px solid var(--border, #cecece); }\r\n &:last-child { border-right: 1px solid var(--border, #cecece); }\r\n }\r\n &:last-child .cell { border-bottom: 1px solid var(--border, #cecece); }\r\n}\r\n.progressHandle { \r\n pointer-events: auto !important; \r\n cursor: ew-resize !important;\r\n}\r\n</style>","<template>\r\n <div v-if='showRow' class=\"barRow\" :style=\"{ height: rowHeight + 'px' }\" @mouseover=\"hoverActive()\"\r\n @mouseleave=\"hoverInactive()\" :class=\"{ active: hover }\" :data-task-id=\"row[mapFields.id]\">\r\n <svg key=\"row.no\" v-if='showRow' ref='bar' class=\"bar\" :height=\"barHeight + 'px'\"\r\n :class=\"{ active: hover }\" style=\"overflow: visible;\"></svg>\r\n <template v-for='(count) in timelineCellCount'\r\n :key=\"count + row.id + timelineCellCount + showRow + '_template'\">\r\n <div class=\"cell\"\r\n :style=\"{ width: scale + 'px', minWidth: scale + 'px', maxWidth: scale + 'px', height: rowHeight + 'px', background: WeekEndColor(count) }\">\r\n </div>\r\n </template>\r\n </div>\r\n</template>\r\n<script lang=\"ts\">\r\nimport { defineComponent, watch, ref, computed, onMounted, onDeactivated, onBeforeUnmount, inject } from 'vue';\r\nimport SVG from 'svg.js';\r\nimport interact from 'interactjs';\r\nimport dayjs from 'dayjs';\r\nimport isoWeek from 'dayjs/plugin/isoWeek';\r\ndayjs.extend(isoWeek);\r\nimport { store, mutations } from './Store';\r\nimport sharedState from '../gantt/ShareState';\r\nimport { Symbols } from './Symbols';\r\n\r\nexport default defineComponent({\r\n name: 'Bar',\r\n emits: ['progress-update'],\r\n props: {\r\n rowHeight: { type: Number as () => number, default: 0 },\r\n row: { type: Object as () => Record<string, any>, default: () => ({}) },\r\n startGanttDate: { type: String as () => string },\r\n endGanttDate: { type: String as () => string }\r\n },\r\n setup(props, { emit }) {\r\n const bar = ref<SVGSVGElement | null>(null);\r\n const barHeight = ref(props.rowHeight * 0.7);\r\n const direction = ref<string | null>(null);\r\n const oldBarDataX = ref(0);\r\n const oldBarWidth = ref(0);\r\n const showRow = ref(true);\r\n const hover = ref(false);\r\n const barColor = ref('');\r\n const isBarInteracted = ref(false);\r\n const themeVersion = ref(0);\r\n const isProgressDragging = ref(false);\r\n\r\n const timelineCellCount = computed(() => store.timelineCellCount);\r\n const scale = computed(() => store.scale);\r\n const mode = computed(() => store.mode);\r\n const mapFields = computed(() => store.mapFields);\r\n const progress = computed(() => {\r\n const progressValue = Number(props.row[mapFields.value.progress]);\r\n if (isNaN(progressValue)) return '0.00%';\r\n return (progressValue * 100).toFixed(2) + '%';\r\n });\r\n\r\n const setBarColor = inject(Symbols.SetBarColorSymbol) as ((row: any) => string) | undefined;\r\n\r\n // 进度更新事件\r\n const emitProgressUpdate = (newProgress: number) => {\r\n const detail = {\r\n taskId: props.row[mapFields.value.id],\r\n oldProgress: Number(props.row[mapFields.value.progress]) || 0,\r\n newProgress: newProgress,\r\n task: props.row\r\n };\r\n emit('progress-update', detail);\r\n window.dispatchEvent(new CustomEvent('taskProgressUpdate', { detail }));\r\n console.log('Task progress updated:', detail);\r\n };\r\n\r\n watch(() => sharedState.highlightedId, (newId) => {\r\n hover.value = props.row[mapFields.value['id']] === newId;\r\n });\r\n\r\n const hoverActive = () => sharedState.triggerHighlight(props.row[mapFields.value.id] as number | null);\r\n const hoverInactive = () => sharedState.triggerHighlight(null);\r\n\r\n const WeekEndColor = (count: number) => {\r\n themeVersion.value;\r\n let bgContent = '#ffffff', bgSecondary = '#f8f8f8';\r\n if (bar.value) {\r\n let element = bar.value.parentElement;\r\n while (element) {\r\n if (element.hasAttribute('data-gantt-theme')) {\r\n bgContent = getComputedStyle(element).getPropertyValue('--bg-content').trim() || '#ffffff';\r\n bgSecondary = getComputedStyle(element).getPropertyValue('--bg-secondary').trim() || '#f8f8f8';\r\n break;\r\n }\r\n element = element.parentElement;\r\n }\r\n }\r\n switch (mode.value) {\r\n case '月': case '日': {\r\n let currentDate = dayjs(props.startGanttDate).add(count, 'days');\r\n return (currentDate.isoWeekday() === 7 || currentDate.isoWeekday() === 1) ? bgSecondary : bgContent;\r\n }\r\n case '周': case '时': return bgContent;\r\n }\r\n };\r\n\r\n const setBarDate = mutations.setBarDate;\r\n const setAllowChangeTaskDate = mutations.setAllowChangeTaskDate;\r\n\r\n const drowBar = (barElement: SVGSVGElement) => {\r\n let dataX = 0;\r\n switch (mode.value) {\r\n case '月': case '日': {\r\n let fromPlanStartDays = dayjs(props.row[mapFields.value.startdate]).diff(dayjs(props.startGanttDate), 'days');\r\n dataX = scale.value * fromPlanStartDays;\r\n let spendDays = dayjs(props.row[mapFields.value.enddate]).diff(dayjs(props.row[mapFields.value.startdate]), 'days') + 1;\r\n oldBarWidth.value = spendDays * scale.value;\r\n props.row[mapFields.value.takestime] = spendDays + '天';\r\n break;\r\n }\r\n case '周': {\r\n const startGanttWeek = dayjs(props.startGanttDate).startOf('isoWeek');\r\n const taskStartWeek = dayjs(props.row[mapFields.value.startdate]).startOf('isoWeek');\r\n let fromPlanStartWeeks = taskStartWeek.diff(startGanttWeek, 'week');\r\n dataX = scale.value * fromPlanStartWeeks;\r\n const taskEndWeek = dayjs(props.row[mapFields.value.enddate]).startOf('isoWeek');\r\n let spendWeeks = taskEndWeek.diff(taskStartWeek, 'week') + 1;\r\n oldBarWidth.value = spendWeeks * scale.value;\r\n props.row[mapFields.value.takestime] = spendWeeks + '周';\r\n break;\r\n }\r\n case '时': {\r\n let fromPlanStartHours = dayjs(props.row[mapFields.value.startdate]).diff(dayjs(props.startGanttDate), 'hours');\r\n dataX = scale.value * fromPlanStartHours;\r\n let spendHours = dayjs(props.row[mapFields.value.enddate]).diff(dayjs(props.row[mapFields.value.startdate]), 'hours') + 1;\r\n oldBarWidth.value = spendHours * scale.value;\r\n props.row[mapFields.value.takestime] = spendHours + '小时';\r\n break;\r\n }\r\n }\r\n oldBarDataX.value = dataX;\r\n let svg = SVG(barElement as unknown as HTMLElement);\r\n const borderColor = getComputedStyle(barElement).getPropertyValue('--border') || '#cecece';\r\n barElement.setAttribute('data-x', dataX.toString());\r\n barElement.setAttribute('width', oldBarWidth.value.toString());\r\n barElement.setAttribute('stroke', borderColor);\r\n barElement.setAttribute('stroke-width', '1px');\r\n barElement.style.transform = `translate(${dataX}px, 0px)`;\r\n\r\n let p = svg.select('pattern').first();\r\n let g = (svg.children().filter((child) => child.type === 'g')[0] as any) || svg.group();\r\n let innerRect = svg.select('.innerRect').first();\r\n let outerRect = svg.select('rect:not(.innerRect):not(.progressHandle)').first();\r\n let text = svg.select('text').first();\r\n let progressHandle = svg.select('.progressHandle').first();\r\n\r\n if (!p) {\r\n p = svg.pattern(10, 10, (add) => {\r\n (add as any).path('M10 -5 -10,15M15,0,0,15M0 -5 -20,15').fill('none').stroke({ color: 'gray', opacity: 0.4, width: 5 });\r\n });\r\n }\r\n if (!g) g = svg.group();\r\n\r\n let innerRectWidth = props.row[mapFields.value.progress] \r\n ? Number(oldBarWidth.value) * Number(props.row[mapFields.value.progress]) \r\n : Number(oldBarWidth.value);\r\n \r\n if (!innerRect) {\r\n innerRect = svg.rect(innerRectWidth, barHeight.value).radius(10);\r\n innerRect.addClass('innerRect');\r\n g.add(innerRect);\r\n } else {\r\n innerRect.fill({ color: barColor.value, opacity: 0.4 });\r\n innerRect.width(innerRectWidth);\r\n }\r\n\r\n if (!outerRect) {\r\n outerRect = svg.rect(oldBarWidth.value, barHeight.value).radius(10).fill(p).stroke({ color: borderColor, width: 1 });\r\n outerRect.on('mouseover', () => outerRect.animate(200).attr({ stroke: '#000', strokeWidth: 2, opacity: 1 }));\r\n outerRect.on('mouseleave', () => outerRect.animate(200).attr({ stroke: '#0066ff', strokeWidth: 10, opacity: 0.4 }));\r\n } else {\r\n outerRect.width(oldBarWidth.value);\r\n }\r\n\r\n if (!text) {\r\n text = svg.text(progress.value).stroke('#faf7ec');\r\n } else {\r\n (text as any).text(progress.value);\r\n }\r\n const textBBox = text.bbox();\r\n (text as any).font({ size: 15, anchor: 'middle', leading: '1em' })\r\n .fill('#000').attr('opacity', 1).attr('dominant-baseline', 'middle')\r\n .center(innerRect.width() / 2 + textBBox.width / 2, innerRect.height() / 2);\r\n\r\n // 进度调节滑块 - 三角形拖拽手柄 + 对齐虚线\r\n const handleWidth = 12;\r\n const handleHeight = 8;\r\n const handleX = innerRectWidth - handleWidth / 2;\r\n // 三角形位置:Bar底部边缘穿过三角形中心\r\n const handleY = barHeight.value - handleHeight / 2;\r\n const lineX = innerRectWidth;\r\n \r\n // 获取主题颜色的辅助函数\r\n const getThemeColors = () => {\r\n let primary = '#f59e0b', primaryDark = '#d97706', primaryLight = '#fbbf24';\r\n let element = barElement.parentElement;\r\n while (element) {\r\n if (element.hasAttribute('data-gantt-theme')) {\r\n primary = getComputedStyle(element).getPropertyValue('--primary').trim() || primary;\r\n primaryDark = getComputedStyle(element).getPropertyValue('--primary-dark').trim() || primaryDark;\r\n primaryLight = getComputedStyle(element).getPropertyValue('--primary-light').trim() || primaryLight;\r\n break;\r\n }\r\n element = element.parentElement;\r\n }\r\n return { primary, primaryDark, primaryLight };\r\n };\r\n \r\n // 查找或创建对齐虚线\r\n let guideLineEl = barElement.querySelector('.progressGuideLine') as SVGLineElement | null;\r\n const themeColors = getThemeColors();\r\n \r\n if (!guideLineEl) {\r\n // 创建对齐虚线\r\n const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');\r\n line.setAttribute('class', 'progressGuideLine');\r\n line.setAttribute('x1', String(lineX));\r\n line.setAttribute('y1', '0');\r\n line.setAttribute('x2', String(lineX));\r\n line.setAttribute('y2', String(handleY));\r\n line.setAttribute('stroke', themeColors.primaryDark);\r\n line.setAttribute('stroke-width', '2');\r\n line.setAttribute('stroke-dasharray', '4,3');\r\n line.setAttribute('opacity', '0.8');\r\n barElement.appendChild(line);\r\n guideLineEl = line;\r\n }\r\n // 始终更新位置和可见性\r\n guideLineEl.setAttribute('x1', String(lineX));\r\n guideLineEl.setAttribute('x2', String(lineX));\r\n guideLineEl.setAttribute('y2', String(handleY));\r\n guideLineEl.setAttribute('stroke', themeColors.primaryDark);\r\n guideLineEl.setAttribute('opacity', '0.8');\r\n \r\n if (!progressHandle) {\r\n // 创建三角形手柄\r\n const trianglePoints = `${handleWidth/2},0 0,${handleHeight} ${handleWidth},${handleHeight}`;\r\n progressHandle = svg.polygon(trianglePoints)\r\n .fill(themeColors.primary)\r\n .stroke({ color: themeColors.primaryDark, width: 1 })\r\n .addClass('progressHandle');\r\n \r\n const handleElement = progressHandle.node as unknown as SVGPolygonElement;\r\n handleElement.style.cursor = 'ew-resize';\r\n handleElement.style.pointerEvents = 'auto';\r\n progressHandle.move(handleX, handleY);\r\n \r\n // 用于记录当前X位置\r\n let currentHandleX = handleX;\r\n \r\n // 悬停效果 - 高亮显示\r\n handleElement.addEventListener('mouseenter', () => {\r\n if (!isProgressDragging.value) {\r\n const colors = getThemeColors();\r\n handleElement.setAttribute('fill', colors.primaryLight);\r\n handleElement.setAttribute('stroke', colors.primary);\r\n handleElement.setAttribute('stroke-width', '2');\r\n guideLineEl!.setAttribute('stroke', colors.primaryLight);\r\n guideLineEl!.setAttribute('stroke-width', '2');\r\n guideLineEl!.setAttribute('opacity', '1');\r\n }\r\n });\r\n handleElement.addEventListener('mouseleave', () => {\r\n if (!isProgressDragging.value) {\r\n const colors = getThemeColors();\r\n handleElement.setAttribute('fill', colors.primary);\r\n handleElement.setAttribute('stroke', colors.primaryDark);\r\n handleElement.setAttribute('stroke-width', '1');\r\n guideLineEl!.setAttribute('stroke', colors.primaryDark);\r\n guideLineEl!.setAttribute('stroke-width', '2');\r\n guideLineEl!.setAttribute('opacity', '0.8');\r\n }\r\n });\r\n \r\n // 拖拽交互\r\n interact(handleElement).draggable({\r\n inertia: false,\r\n listeners: {\r\n start: () => { \r\n isProgressDragging.value = true;\r\n const colors = getThemeColors();\r\n handleElement.setAttribute('fill', colors.primaryDark);\r\n handleElement.setAttribute('stroke', colors.primaryDark);\r\n handleElement.setAttribute('stroke-width', '2');\r\n guideLineEl!.setAttribute('stroke', colors.primaryDark);\r\n guideLineEl!.setAttribute('stroke-width', '2');\r\n guideLineEl!.setAttribute('opacity', '1');\r\n },\r\n move: (event) => {\r\n currentHandleX += event.dx;\r\n currentHandleX = Math.max(-handleWidth / 2, Math.min(currentHandleX, oldBarWidth.value - handleWidth / 2));\r\n \r\n progressHandle.move(currentHandleX, handleY);\r\n \r\n // 更新虚线位置\r\n const newLineX = currentHandleX + handleWidth / 2;\r\n guideLineEl!.setAttribute('x1', String(newLineX));\r\n guideLineEl!.setAttribute('x2', String(newLineX));\r\n \r\n // 更新进度\r\n const newProgress = Math.min(1, Math.max(0, (currentHandleX + handleWidth / 2) / oldBarWidth.value));\r\n innerRect.width(newProgress * oldBarWidth.value);\r\n (text as any).text((newProgress * 100).toFixed(2) + '%');\r\n (text as any).center(innerRect.width() / 2 + textBBox.width / 2, innerRect.height() / 2);\r\n },\r\n end: () => {\r\n isProgressDragging.value = false;\r\n const colors = getThemeColors();\r\n handleElement.setAttribute('fill', colors.primary);\r\n handleElement.setAttribute('stroke', colors.primaryDark);\r\n handleElement.setAttribute('stroke-width', '1');\r\n guideLineEl!.setAttribute('stroke', colors.primaryDark);\r\n guideLineEl!.setAttribute('stroke-width', '2');\r\n guideLineEl!.setAttribute('opacity', '0.8');\r\n \r\n const newProgress = Math.min(1, Math.max(0, (currentHandleX + handleWidth / 2) / oldBarWidth.value));\r\n props.row[mapFields.value.progress] = newProgress;\r\n emitProgressUpdate(newProgress);\r\n }\r\n }\r\n });\r\n } else {\r\n progressHandle.move(handleX, handleY);\r\n // 更新已存在的三角形颜色以响应主题变化\r\n const handleElement = progressHandle.node as unknown as SVGPolygonElement;\r\n handleElement.setAttribute('fill', themeColors.primary);\r\n handleElement.setAttribute('stroke', themeColors.primaryDark);\r\n }\r\n\r\n setBarDate({ id: props.row[mapFields.value.id], startDate: props.row[mapFields.value.startdate], endDate: props.row[mapFields.value.enddate] });\r\n\r\n // 拖拽功能\r\n interact(barElement).draggable({\r\n inertia: false,\r\n modifiers: [interact.modifiers.restrictRect({ restriction: 'parent', endOnly: true })],\r\n autoScroll: true,\r\n listeners: {\r\n start: (event: { target: SVGSVGElement }) => {\r\n if (isProgressDragging.value) return;\r\n oldBarDataX.value = Number(event.target.getAttribute('data-x'));\r\n oldBarWidth.value = event.target.width.baseVal.value;\r\n },\r\n move: (event: { target: SVGSVGElement; dx: number; rect: { width: number; height: number } }) => {\r\n if (isProgressDragging.value) return;\r\n let x = ((parseFloat(event.target.getAttribute('data-x') || '0') || 0) + event.dx).toString();\r\n Object.assign(event.target.style, { width: `${event.rect.width}px`, height: `${event.rect.height}px`, transform: `translate(${x}px, 0px)` });\r\n event.target.setAttribute('data-x', x);\r\n event.target.setAttribute('data-y', '0');\r\n },\r\n end: (event: { target: SVGSVGElement }) => {\r\n if (isProgressDragging.value) return;\r\n let target = event.target;\r\n let currentX = parseFloat(target.getAttribute('data-x') || '0') || 0;\r\n let multiple = Math.round(currentX / scale.value);\r\n let alignedX = multiple * scale.value;\r\n if (alignedX < 0) alignedX = 0;\r\n if (alignedX > timelineCellCount.value * scale.value) alignedX = timelineCellCount.value * scale.value;\r\n \r\n const parentIdField = mapFields.value.parentId || 'pid';\r\n const currentParentId = props.row[parentIdField];\r\n if (currentParentId && currentParentId !== '0') {\r\n const parentTask = store.tasks.find(t => String(t[mapFields.value.id]) === String(currentParentId));\r\n if (parentTask) {\r\n let parentStartX = 0;\r\n if (mode.value === '月' || mode.value === '日') {\r\n parentStartX = dayjs(parentTask[mapFields.value.startdate]).diff(dayjs(props.startGanttDate), 'days') * scale.value;\r\n } else if (mode.value === '周') {\r\n const ganttStartWeek = dayjs(props.startGanttDate).startOf('isoWeek');\r\n const parentStartWeek = dayjs(parentTask[mapFields.value.startdate]).startOf('isoWeek');\r\n parentStartX = parentStartWeek.diff(ganttStartWeek, 'week') * scale.value;\r\n } else if (mode.value === '时') {\r\n parentStartX = dayjs(parentTask[mapFields.value.startdate]).diff(dayjs(props.startGanttDate), 'hours') * scale.value;\r\n }\r\n if (alignedX < parentStartX) alignedX = parentStartX;\r\n }\r\n }\r\n \r\n target.style.transform = `translate(${alignedX}px, 0px)`;\r\n target.setAttribute('data-x', alignedX.toString());\r\n \r\n const cellsMoved = Math.round((alignedX - oldBarDataX.value) / scale.value);\r\n let daysOffset = 0, hoursOffset = 0;\r\n if (mode.value === '月' || mode.value === '日') daysOffset = cellsMoved;\r\n else if (mode.value === '周') daysOffset = cellsMoved * 7;\r\n else if (mode.value === '时') hoursOffset = cellsMoved;\r\n \r\n if (mode.value === '时') {\r\n props.row[mapFields.value.startdate] = dayjs(props.row[mapFields.value.startdate]).add(hoursOffset, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n props.row[mapFields.value.enddate] = dayjs(props.row[mapFields.value.enddate]).add(hoursOffset, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n props.row[mapFields.value.takestime] = (dayjs(props.row[mapFields.value.enddate]).diff(dayjs(props.row[mapFields.value.startdate]), 'hours') + 1) + '小时';\r\n } else {\r\n props.row[mapFields.value.startdate] = dayjs(props.row[mapFields.value.startdate]).add(daysOffset, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n props.row[mapFields.value.enddate] = dayjs(props.row[mapFields.value.enddate]).add(daysOffset, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n if (mode.value === '月' || mode.value === '日') {\r\n props.row[mapFields.value.takestime] = (dayjs(props.row[mapFields.value.enddate]).diff(dayjs(props.row[mapFields.value.startdate]), 'days') + 1) + '天';\r\n } else if (mode.value === '周') {\r\n const startWeek = dayjs(props.row[mapFields.value.startdate]).startOf('isoWeek');\r\n const endWeek = dayjs(props.row[mapFields.value.enddate]).startOf('isoWeek');\r\n props.row[mapFields.value.takestime] = (endWeek.diff(startWeek, 'week') + 1) + '周';\r\n }\r\n }\r\n \r\n // 联动子任务\r\n const newParentStartDate = dayjs(props.row[mapFields.value.startdate]);\r\n const currentTaskId = props.row[mapFields.value.id];\r\n const getAllChildren = (parentId: any, tasks: any[]): any[] => {\r\n const children: any[] = [];\r\n for (const task of tasks) {\r\n if (String(task[parentIdField]) === String(parentId)) {\r\n children.push(task);\r\n children.push(...getAllChildren(task[mapFields.value.id], tasks));\r\n }\r\n }\r\n return children;\r\n };\r\n const childTasks = getAllChildren(currentTaskId, store.tasks);\r\n const actualOffset = mode.value === '时' ? hoursOffset : daysOffset;\r\n if (actualOffset > 0) {\r\n for (const child of childTasks) {\r\n if (dayjs(child[mapFields.value.startdate]).isBefore(newParentStartDate)) {\r\n if (mode.value === '时') {\r\n child[mapFields.value.startdate] = dayjs(child[mapFields.value.startdate]).add(hoursOffset, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n child[mapFields.value.enddate] = dayjs(child[mapFields.value.enddate]).add(hoursOffset, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n } else {\r\n child[mapFields.value.startdate] = dayjs(child[mapFields.value.startdate]).add(daysOffset, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n child[mapFields.value.enddate] = dayjs(child[mapFields.value.enddate]).add(daysOffset, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n }\r\n setBarDate({ id: child[mapFields.value.id], startDate: child[mapFields.value.startdate], endDate: child[mapFields.value.enddate] });\r\n }\r\n }\r\n }\r\n setBarDate({ id: props.row[mapFields.value.id], startDate: props.row[mapFields.value.startdate], endDate: props.row[mapFields.value.enddate] });\r\n }\r\n }\r\n });\r\n\r\n // 调整大小功能\r\n interact(barElement).resizable({\r\n edges: { left: true, right: true, bottom: false, top: false },\r\n listeners: {\r\n start: (event: { target: SVGSVGElement }) => {\r\n oldBarDataX.value = Number(event.target.getAttribute('data-x'));\r\n oldBarWidth.value = Number(event.target.getAttribute('width'));\r\n },\r\n end: (event: { target: SVGSVGElement; rect: { width: number }; edges: { left: boolean; right: boolean } }) => {\r\n setAllowChangeTaskDate(props.row);\r\n let target = event.target;\r\n let newWidth = event.rect.width;\r\n let widthCells = Math.round(newWidth / scale.value);\r\n if (widthCells < 1) widthCells = 1;\r\n newWidth = widthCells * scale.value;\r\n let currentX = oldBarDataX.value;\r\n \r\n if (event.edges && event.edges.left) {\r\n currentX = oldBarDataX.value + (oldBarWidth.value - newWidth);\r\n currentX = Math.round(currentX / scale.value) * scale.value;\r\n if (currentX < 0) currentX = 0;\r\n \r\n const parentIdField = mapFields.value.parentId || 'pid';\r\n const currentParentId = props.row[parentIdField];\r\n if (currentParentId && currentParentId !== '0') {\r\n const parentTask = store.tasks.find(t => String(t[mapFields.value.id]) === String(currentParentId));\r\n if (parentTask) {\r\n let parentStartX = 0;\r\n if (mode.value === '月' || mode.value === '日') {\r\n parentStartX = dayjs(parentTask[mapFields.value.startdate]).diff(dayjs(props.startGanttDate), 'days') * scale.value;\r\n } else if (mode.value === '周') {\r\n const ganttStartWeek = dayjs(props.startGanttDate).startOf('isoWeek');\r\n const parentStartWeek = dayjs(parentTask[mapFields.value.startdate]).startOf('isoWeek');\r\n parentStartX = parentStartWeek.diff(ganttStartWeek, 'week') * scale.value;\r\n } else if (mode.value === '时') {\r\n parentStartX = dayjs(parentTask[mapFields.value.startdate]).diff(dayjs(props.startGanttDate), 'hours') * scale.value;\r\n }\r\n if (currentX < parentStartX) {\r\n currentX = parentStartX;\r\n newWidth = oldBarDataX.value + oldBarWidth.value - currentX;\r\n widthCells = Math.round(newWidth / scale.value);\r\n if (widthCells < 1) widthCells = 1;\r\n newWidth = widthCells * scale.value;\r\n }\r\n }\r\n }\r\n }\r\n \r\n target.setAttribute('width', newWidth.toString());\r\n target.style.width = newWidth + 'px';\r\n target.style.transform = `translate(${currentX}px, 0px)`;\r\n target.setAttribute('data-x', currentX.toString());\r\n \r\n const startCellIndex = Math.round(currentX / scale.value);\r\n const endCellIndex = startCellIndex + widthCells - 1;\r\n let newStartDate: string, newEndDate: string;\r\n \r\n if (mode.value === '月' || mode.value === '日') {\r\n newStartDate = dayjs(props.startGanttDate).add(startCellIndex, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n newEndDate = dayjs(props.startGanttDate).add(endCellIndex, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n props.row[mapFields.value.takestime] = widthCells + '天';\r\n } else if (mode.value === '周') {\r\n const ganttStartWeek = dayjs(props.startGanttDate).startOf('isoWeek');\r\n newStartDate = ganttStartWeek.add(startCellIndex, 'weeks').format('YYYY-MM-DD HH:mm:ss');\r\n newEndDate = ganttStartWeek.add(endCellIndex, 'weeks').endOf('isoWeek').format('YYYY-MM-DD HH:mm:ss');\r\n props.row[mapFields.value.takestime] = widthCells + '周';\r\n } else {\r\n newStartDate = dayjs(props.startGanttDate).add(startCellIndex, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n newEndDate = dayjs(props.startGanttDate).add(endCellIndex, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n props.row[mapFields.value.takestime] = widthCells + '小时';\r\n }\r\n \r\n props.row[mapFields.value.startdate] = newStartDate;\r\n props.row[mapFields.value.enddate] = newEndDate;\r\n \r\n let svg = SVG(barElement as unknown as HTMLElement);\r\n let innerRect = svg.select('.innerRect').first();\r\n let outerRect = svg.select('rect:not(.innerRect):not(.progressHandle)').first();\r\n if (innerRect) {\r\n let innerRectWidth = props.row[mapFields.value.progress] ? newWidth * Number(props.row[mapFields.value.progress]) : newWidth;\r\n innerRect.width(innerRectWidth);\r\n }\r\n if (outerRect) outerRect.width(newWidth);\r\n \r\n // 联动子任务\r\n if (event.edges && event.edges.left) {\r\n const newParentStartDate = dayjs(newStartDate);\r\n const currentTaskId = props.row[mapFields.value.id];\r\n const parentIdField = mapFields.value.parentId || 'pid';\r\n const getAllChildren = (parentId: any, tasks: any[]): any[] => {\r\n const children: any[] = [];\r\n for (const task of tasks) {\r\n if (String(task[parentIdField]) === String(parentId)) {\r\n children.push(task);\r\n children.push(...getAllChildren(task[mapFields.value.id], tasks));\r\n }\r\n }\r\n return children;\r\n };\r\n const childTasks = getAllChildren(currentTaskId, store.tasks);\r\n for (const child of childTasks) {\r\n if (dayjs(child[mapFields.value.startdate]).isBefore(newParentStartDate)) {\r\n const childOffset = newParentStartDate.diff(dayjs(child[mapFields.value.startdate]), mode.value === '时' ? 'hours' : 'days');\r\n if (mode.value === '时') {\r\n child[mapFields.value.startdate] = dayjs(child[mapFields.value.startdate]).add(childOffset, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n child[mapFields.value.enddate] = dayjs(child[mapFields.value.enddate]).add(childOffset, 'hours').format('YYYY-MM-DD HH:mm:ss');\r\n } else {\r\n child[mapFields.value.startdate] = dayjs(child[mapFields.value.startdate]).add(childOffset, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n child[mapFields.value.enddate] = dayjs(child[mapFields.value.enddate]).add(childOffset, 'days').format('YYYY-MM-DD HH:mm:ss');\r\n }\r\n setBarDate({ id: child[mapFields.value.id], startDate: child[mapFields.value.startdate], endDate: child[mapFields.value.enddate] });\r\n }\r\n }\r\n }\r\n setBarDate({ id: props.row[mapFields.value.id], startDate: newStartDate, endDate: newEndDate });\r\n }\r\n },\r\n modifiers: [\r\n interact.modifiers.restrictEdges({ outer: 'parent' }),\r\n interact.modifiers.restrictSize({ min: { width: scale.value, height: barHeight.value } })\r\n ],\r\n inertia: false, hold: 1\r\n });\r\n };\r\n\r\n watch(() => [props.row[mapFields.value.startdate], props.row[mapFields.value.enddate]], () => {\r\n if (bar.value && isBarInteracted.value) drowBar(bar.value);\r\n }, { deep: false });\r\n\r\n onMounted(() => {\r\n console.log(`[Bar] Component mounted, task: ${props.row[mapFields.value.id]}, mode: ${store.mode}`);\r\n if (bar.value && !isBarInteracted.value) {\r\n drowBar(bar.value);\r\n isBarInteracted.value = true;\r\n }\r\n if (setBarColor) {\r\n barColor.value = setBarColor(props.row);\r\n if (bar.value) drowBar(bar.value);\r\n }\r\n // 监听主题变化\r\n if (bar.value) {\r\n let ganttContainer = bar.value.parentElement;\r\n while (ganttContainer && !ganttContainer.hasAttribute('data-gantt-theme')) {\r\n ganttContainer = ganttContainer.parentElement;\r\n }\r\n if (ganttContainer) {\r\n const observer = new MutationObserver(() => { themeVersion.value++; });\r\n observer.observe(ganttContainer, { attributes: true, attributeFilter: ['data-gantt-theme', 'style'] });\r\n onBeforeUnmount(() => observer.disconnect());\r\n }\r\n }\r\n });\r\n\r\n onDeactivated(() => {\r\n if (bar.value) { try { interact(bar.value).unset(); } catch (e) {} }\r\n showRow.value = false;\r\n });\r\n\r\n onBeforeUnmount(() => {\r\n if (bar.value) { try { interact(bar.value).unset(); } catch (e) {} }\r\n showRow.value = false;\r\n });\r\n\r\n return {\r\n bar, barHeight, direction, oldBarDataX, oldBarWidth, showRow, hover, barColor,\r\n timelineCellCount, scale, mode, mapFields, hoverActive, hoverInactive, WeekEndColor\r\n };\r\n },\r\n})\r\n</script>\r\n<style lang=\"scss\" scoped>\r\n.barRow.active { background: var(--row-hover, #FFF3A1) !important; .cell { background: var(--row-hover, #FFF3A1) !important; } }\r\n.barRow {\r\n display: flex; flex-flow: row nowrap; align-items: center; justify-content: flex-start;\r\n width: fit-content; position: relative; overflow: visible;\r\n .bar { position: absolute; z-index: 100; background-color: #faf7ec; border-radius: 10px; overflow: visible; }\r\n &:first-child .cell {\r\n border-top: none; display: flex; align-items: center; justify-content: center; font-size: 10px;\r\n position: relative; margin: 0; box-sizing: border-box;\r\n &:first-child { border-left: 1px solid var(--border, #cecece); }\r\n &:not(:last-child) { border-right: 1px solid var(--border, #cecece); }\r\n &:last-child { border-right: 1px solid var(--border, #cecece); }\r\n }\r\n &:not(:first-child) .cell {\r\n border-top: 1px solid var(--border, #cecece); display: flex; align-items: center; justify-content: center;\r\n font-size: 10px; position: relative; margin: 0; box-sizing: border-box;\r\n &:first-child { border-left: 1px solid var(--border, #cecece); }\r\n &:not(:last-child) { border-right: 1px solid var(--border, #cecece); }\r\n &:last-child { border-right: 1px solid var(--border, #cecece); }\r\n }\r\n &:last-child .cell { border-bottom: 1px solid var(--border, #cecece); }\r\n}\r\n.progressHandle { \r\n pointer-events: auto !important; \r\n cursor: ew-resize !important;\r\n}\r\n</style>","<template>\r\n <div>\r\n <template v-for=\"(item) in filterTask\" :key=\"item.id\">\r\n <Bar \r\n :startGanttDate=\"startGanttDate\" \r\n :endGanttDate=\"endGanttDate\" \r\n :row=\"item\" \r\n :rowHeight=\"rowHeight\" \r\n />\r\n </template>\r\n </div>\r\n </template>\r\n \r\n <script lang=\"ts\">\r\n import { defineComponent, ref, computed, watch } from 'vue';\r\n import { store, mutations } from '../gantt/Store';\r\n import Bar from '../gantt/Bar.vue';\r\n \r\n export default defineComponent({\r\n name: 'BarRecursionRow',\r\n props: {\r\n rowHeight: {\r\n type: Number as () => number,\r\n default: 0\r\n },\r\n },\r\n setup() {\r\n \r\n const hiddenTask = ref<any[]>([]);\r\n const componentKey = ref(0);\r\n \r\n const allTask = computed(() => store.tasks);\r\n const timelineCellCount = computed(() => store.timelineCellCount);\r\n const scale = computed(() => store.scale);\r\n const mode = computed(() => store.mode);\r\n const startGanttDate = computed(() => store.startGanttDate ? store.startGanttDate.toISOString() : undefined);\r\n const endGanttDate = computed(() => store.endGanttDate ? store.endGanttDate.toISOString() : undefined);\r\n const mapFields = computed(() => store.mapFields);\r\n const collapsedTasks = computed(() => store.collapsedTasks);\r\n \r\n // 获取所有被折叠的子任务\r\n const getAllCollapsedChildren = (parentId: any): Set<any> => {\r\n const collapsedChildren = new Set<any>();\r\n \r\n const collectChildren = (pid: any) => {\r\n const children = allTask.value.filter(task => task[mapFields.value['parentId']] === pid);\r\n children.forEach(child => {\r\n const childId = child[mapFields.value['id']];\r\n collapsedChildren.add(childId);\r\n // 递归收集所有子孙任务\r\n collectChildren(childId);\r\n });\r\n };\r\n \r\n collectChildren(parentId);\r\n return collapsedChildren;\r\n };\r\n \r\n // 优化:使用Set提高查找性能\r\n const hiddenTaskIds = computed(() => {\r\n return new Set(hiddenTask.value.map(obj => obj[mapFields.value.id]));\r\n });\r\n\r\n const filterTask = computed(() => {\r\n const hiddenIds = hiddenTaskIds.value;\r\n const tasks = store.tasks.filter(task => !hiddenIds.has(task[mapFields.value.id]));\r\n \r\n // 过滤折叠的子任务\r\n const allCollapsedIds = new Set<any>();\r\n collapsedTasks.value.forEach(collapsedId => {\r\n const children = getAllCollapsedChildren(collapsedId);\r\n children.forEach(childId => allCollapsedIds.add(childId));\r\n });\r\n \r\n return tasks.filter(task => !allCollapsedIds.has(task[mapFields.value.id]));\r\n });\r\n \r\n const expandRow = computed({\r\n get: () => store.expandRow,\r\n set: (newValue) => {\r\n mutations.setExpandRow(newValue);\r\n }\r\n });\r\n \r\n watch(expandRow, (newVal) => {\r\n hiddenTask.value = [];\r\n recursionRow(newVal.pid);\r\n });\r\n \r\n const recursionRow = (id: any) => {\r\n let findRows = allTask.value.filter(obj => obj[mapFields.value['parentId']] === id);\r\n if (findRows && findRows.length > 0) {\r\n for (let i = 0; i < findRows.length; i++) {\r\n if (expandRow.value.expand === false) {\r\n hiddenTask.value.push(findRows[i]);\r\n }\r\n recursionRow(findRows[i][mapFields.value['id']]);\r\n }\r\n }\r\n };\r\n \r\n const setExpandRow = mutations.setExpandRow;\r\n \r\n return {\r\n hiddenTask,\r\n componentKey,\r\n allTask,\r\n timelineCellCount,\r\n scale,\r\n mode,\r\n startGanttDate,\r\n endGanttDate,\r\n mapFields,\r\n filterTask,\r\n expandRow,\r\n setExpandRow,\r\n recursionRow\r\n };\r\n },\r\n components: { Bar },\r\n mounted() {\r\n this.$nextTick(() => {\r\n // 可以在这里添加挂载后的逻辑\r\n });\r\n },\r\n activated() {\r\n this.componentKey++;\r\n }\r\n });\r\n </script>","<template>\r\n <div>\r\n <template v-for=\"(item) in filterTask\" :key=\"item.id\">\r\n <Bar \r\n :startGanttDate=\"startGanttDate\" \r\n :endGanttDate=\"endGanttDate\" \r\n :row=\"item\" \r\n :rowHeight=\"rowHeight\" \r\n />\r\n </template>\r\n </div>\r\n </template>\r\n \r\n <script lang=\"ts\">\r\n import { defineComponent, ref, computed, watch } from 'vue';\r\n import { store, mutations } from '../gantt/Store';\r\n import Bar from '../gantt/Bar.vue';\r\n \r\n export default defineComponent({\r\n name: 'BarRecursionRow',\r\n props: {\r\n rowHeight: {\r\n type: Number as () => number,\r\n default: 0\r\n },\r\n },\r\n setup() {\r\n \r\n const hiddenTask = ref<any[]>([]);\r\n const componentKey = ref(0);\r\n \r\n const allTask = computed(() => store.tasks);\r\n const timelineCellCount = computed(() => store.timelineCellCount);\r\n const scale = computed(() => store.scale);\r\n const mode = computed(() => store.mode);\r\n const startGanttDate = computed(() => store.startGanttDate ? store.startGanttDate.toISOString() : undefined);\r\n const endGanttDate = computed(() => store.endGanttDate ? store.endGanttDate.toISOString() : undefined);\r\n const mapFields = computed(() => store.mapFields);\r\n const collapsedTasks = computed(() => store.collapsedTasks);\r\n \r\n // 获取所有被折叠的子任务\r\n const getAllCollapsedChildren = (parentId: any): Set<any> => {\r\n const collapsedChildren = new Set<any>();\r\n \r\n const collectChildren = (pid: any) => {\r\n const children = allTask.value.filter(task => task[mapFields.value['parentId']] === pid);\r\n children.forEach(child => {\r\n const childId = child[mapFields.value['id']];\r\n collapsedChildren.add(childId);\r\n // 递归收集所有子孙任务\r\n collectChildren(childId);\r\n });\r\n };\r\n \r\n collectChildren(parentId);\r\n return collapsedChildren;\r\n };\r\n \r\n // 优化:使用Set提高查找性能\r\n const hiddenTaskIds = computed(() => {\r\n return new Set(hiddenTask.value.map(obj => obj[mapFields.value.id]));\r\n });\r\n\r\n const filterTask = computed(() => {\r\n const hiddenIds = hiddenTaskIds.value;\r\n const tasks = store.tasks.filter(task => !hiddenIds.has(task[mapFields.value.id]));\r\n \r\n // 过滤折叠的子任务\r\n const allCollapsedIds = new Set<any>();\r\n collapsedTasks.value.forEach(collapsedId => {\r\n const children = getAllCollapsedChildren(collapsedId);\r\n children.forEach(childId => allCollapsedIds.add(childId));\r\n });\r\n \r\n return tasks.filter(task => !allCollapsedIds.has(task[mapFields.value.id]));\r\n });\r\n \r\n const expandRow = computed({\r\n get: () => store.expandRow,\r\n set: (newValue) => {\r\n mutations.setExpandRow(newValue);\r\n }\r\n });\r\n \r\n watch(expandRow, (newVal) => {\r\n hiddenTask.value = [];\r\n recursionRow(newVal.pid);\r\n });\r\n \r\n const recursionRow = (id: any) => {\r\n let findRows = allTask.value.filter(obj => obj[mapFields.value['parentId']] === id);\r\n if (findRows && findRows.length > 0) {\r\n for (let i = 0; i < findRows.length; i++) {\r\n if (expandRow.value.expand === false) {\r\n hiddenTask.value.push(findRows[i]);\r\n }\r\n recursionRow(findRows[i][mapFields.value['id']]);\r\n }\r\n }\r\n };\r\n \r\n const setExpandRow = mutations.setExpandRow;\r\n \r\n return {\r\n hiddenTask,\r\n componentKey,\r\n allTask,\r\n timelineCellCount,\r\n scale,\r\n mode,\r\n startGanttDate,\r\n endGanttDate,\r\n mapFields,\r\n filterTask,\r\n expandRow,\r\n setExpandRow,\r\n recursionRow\r\n };\r\n },\r\n components: { Bar },\r\n mounted() {\r\n this.$nextTick(() => {\r\n // 可以在这里添加挂载后的逻辑\r\n });\r\n },\r\n activated() {\r\n this.componentKey++;\r\n }\r\n });\r\n </script>","<template>\r\n <svg \r\n class=\"task-links-layer\" \r\n :width=\"containerWidth\" \r\n :height=\"containerHeight\"\r\n :style=\"{ \r\n position: 'absolute', \r\n top: '0px', \r\n left: '0px', \r\n pointerEvents: 'none',\r\n zIndex: 50,\r\n overflow: 'visible'\r\n }\"\r\n >\r\n <!-- 渲染所有连线 -->\r\n <g v-for=\"link in links\" :key=\"link.id\">\r\n <!-- 连线路径 -->\r\n <path\r\n :d=\"link.path\"\r\n :stroke=\"getLinkStyle(link.type).color\"\r\n :stroke-width=\"getLinkStyle(link.type).width\"\r\n :stroke-dasharray=\"getLinkStyle(link.type).dashArray\"\r\n fill=\"none\"\r\n :class=\"['task-link', link.type, { \r\n 'dash-animated': isDashedLine(link.type) && linkConfig.enableDashAnimation \r\n }]\"\r\n :style=\"getDashAnimationStyle(link)\"\r\n />\r\n \r\n <!-- 箭头 -->\r\n <polygon\r\n v-if=\"linkConfig.showArrow && link.arrowPoints && link.arrowPoints.length > 0\"\r\n :points=\"link.arrowPoints\"\r\n :fill=\"getLinkStyle(link.type).color\"\r\n :stroke=\"getLinkStyle(link.type).color\"\r\n :stroke-width=\"0.5\"\r\n class=\"task-link-arrow\"\r\n />\r\n \r\n <!-- 连线标签(可选) -->\r\n <text\r\n v-if=\"link.label && linkConfig.showLabels\"\r\n :x=\"link.labelX\"\r\n :y=\"link.labelY\"\r\n :fill=\"linkConfig.labelColor\"\r\n :font-size=\"linkConfig.labelFontSize\"\r\n text-anchor=\"middle\"\r\n class=\"task-link-label\"\r\n >\r\n {{ link.label }}\r\n </text>\r\n </g>\r\n </svg>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, watch, ref, onMounted, onUnmounted } from 'vue';\r\nimport { store } from './Store';\r\nimport { LinkType, LinkPathType, type LinkConfig, type TaskLink } from './Types';\r\nimport { linkDataManager } from './LinkConfig';\r\n\r\n\r\n\r\nexport default defineComponent({\r\n name: 'TaskLinks',\r\n props: {\r\n containerWidth: {\r\n type: Number,\r\n required: true\r\n },\r\n containerHeight: {\r\n type: Number,\r\n required: true\r\n },\r\n linkConfig: {\r\n type: Object as () => LinkConfig,\r\n default: () => ({\r\n color: '#3498db',\r\n width: 2,\r\n dashArray: undefined,\r\n showArrow: true,\r\n arrowColor: undefined,\r\n arrowSize: 8,\r\n showLabels: false,\r\n labelColor: '#666',\r\n labelFontSize: 12,\r\n cornerRadius: 5,\r\n pathType: LinkPathType.BEZIER,\r\n bezierCurvature: 0.4,\r\n rightAngleOffset: 30,\r\n smoothCorners: true,\r\n enableDashAnimation: true,\r\n dashAnimationSpeed: 0.8,\r\n parentChildStyle: {\r\n color: '#95a5a6',\r\n width: 1,\r\n dashArray: '3,3'\r\n }\r\n })\r\n }\r\n },\r\n setup(props) {\r\n const links = ref<TaskLink[]>([]);\r\n \r\n // 获取任务位置信息\r\n const getTaskPosition = (taskId: string) => {\r\n const task = store.tasks.find(t => t[store.mapFields.id] === taskId);\r\n if (!task) return null;\r\n \r\n // 查找对应的DOM元素 - 查找barRow而不是bar\r\n const barElement = document.querySelector(`[data-task-id=\"${taskId}\"]`) as HTMLElement;\r\n if (!barElement) return null;\r\n \r\n // 查找SVG bar元素获取实际位置\r\n const svgBar = barElement.querySelector('.bar') as SVGSVGElement;\r\n if (!svgBar) return null;\r\n \r\n // 获取容器 - 使用更精确的选择器\r\n const rightTableContent = barElement.closest('.table .content') as HTMLElement;\r\n if (!rightTableContent) return null;\r\n \r\n // 获取SVG的transform属性\r\n const dataX = parseFloat(svgBar.getAttribute('data-x') || '0');\r\n const barWidth = svgBar.width.baseVal.value;\r\n \r\n // 获取任务在当前容器中的位置\r\n const barRowRect = barElement.getBoundingClientRect();\r\n const containerRect = rightTableContent.getBoundingClientRect();\r\n \r\n // 计算任务相对于容器的位置\r\n const relativeY = barRowRect.top - containerRect.top;\r\n \r\n return {\r\n x: dataX,\r\n y: relativeY,\r\n width: barWidth,\r\n height: barRowRect.height,\r\n centerX: dataX + barWidth / 2,\r\n centerY: relativeY + barRowRect.height / 2,\r\n rightX: dataX + barWidth,\r\n leftX: dataX,\r\n topY: relativeY,\r\n bottomY: relativeY + barRowRect.height\r\n };\r\n };\r\n \r\n // 获取父任务的子任务索引(用于计算偏移)\r\n const getChildIndex = (parentId: string, childId: string): number => {\r\n const siblings = store.tasks.filter(task => \r\n task[store.mapFields.parentId] === parentId && \r\n task[store.mapFields.parentId] !== '0'\r\n );\r\n return siblings.findIndex(task => task[store.mapFields.id] === childId);\r\n };\r\n \r\n\r\n \r\n // 获取行高(从DOM或props中获取)\r\n const getRowHeight = (): number => {\r\n // 尝试从第一个barRow元素获取高度\r\n const firstBarRow = document.querySelector('.barRow') as HTMLElement;\r\n if (firstBarRow) {\r\n return firstBarRow.offsetHeight;\r\n }\r\n // 默认行高\r\n return 50;\r\n };\r\n \r\n // 将Y坐标对齐到最近的单元格中心线\r\n const alignToGridCenter = (y: number): number => {\r\n const rowHeight = getRowHeight();\r\n const rowIndex = Math.floor(y / rowHeight);\r\n return rowIndex * rowHeight + rowHeight / 2;\r\n };\r\n \r\n // 根据子任务索引获取对应行的中心线位置(只能向下)\r\n const getChildRowCenter = (parentY: number, childIndex: number): number => {\r\n const rowHeight = getRowHeight();\r\n // 从父任务下方开始,第一个子任务在第一行,第二个在第二行,以此类推\r\n const parentRowIndex = Math.floor(parentY / rowHeight);\r\n const targetRowIndex = parentRowIndex + 1 + childIndex;\r\n return targetRowIndex * rowHeight + rowHeight / 2;\r\n };\r\n \r\n // 确保绕行路径只能向下(不能高过父任务)\r\n const getSafeBypassY = (parentY: number, childY: number, gap: number): number => {\r\n const rowHeight = getRowHeight();\r\n const parentBottomY = Math.floor(parentY / rowHeight) * rowHeight + rowHeight;\r\n const minBypassY = parentBottomY + gap;\r\n \r\n // 如果子任务在父任务下方很远,可以选择一个中间位置\r\n if (childY > minBypassY + rowHeight) {\r\n return alignToGridCenter(minBypassY + rowHeight / 2);\r\n }\r\n \r\n return alignToGridCenter(minBypassY);\r\n };\r\n \r\n // 获取连线类型对应的颜色(从配置中读取)\r\n const getLinkTypeColor = (linkType: LinkType): string => {\r\n const colors = props.linkConfig.linkTypeColors;\r\n if (!colors) {\r\n // 默认颜色\r\n const defaultColors: Record<LinkType, string> = {\r\n [LinkType.FINISH_TO_START]: '#3498db',\r\n [LinkType.START_TO_START]: '#2ecc71',\r\n [LinkType.FINISH_TO_FINISH]: '#e74c3c',\r\n [LinkType.START_TO_FINISH]: '#f39c12',\r\n [LinkType.PARENT_CHILD]: '#95a5a6'\r\n };\r\n return defaultColors[linkType] || props.linkConfig.color;\r\n }\r\n \r\n switch (linkType) {\r\n case LinkType.FINISH_TO_START:\r\n return colors.finishToStart || '#3498db';\r\n case LinkType.START_TO_START:\r\n return colors.startToStart || '#2ecc71';\r\n case LinkType.FINISH_TO_FINISH:\r\n return colors.finishToFinish || '#e74c3c';\r\n case LinkType.START_TO_FINISH:\r\n return colors.startToFinish || '#f39c12';\r\n default:\r\n return props.linkConfig.color;\r\n }\r\n };\r\n\r\n // 获取特定连线类型的样式\r\n const getLinkStyle = (linkType: LinkType) => {\r\n if (linkType === LinkType.PARENT_CHILD) {\r\n return {\r\n color: props.linkConfig.parentChildStyle.color || '#95a5a6',\r\n width: props.linkConfig.parentChildStyle.width,\r\n dashArray: props.linkConfig.parentChildStyle.dashArray\r\n };\r\n }\r\n \r\n // 任务依赖连线使用对应类型的颜色\r\n return {\r\n color: getLinkTypeColor(linkType),\r\n width: props.linkConfig.width,\r\n dashArray: props.linkConfig.dashArray\r\n };\r\n };\r\n \r\n // 生成连线路径\r\n const generateLinkPath = (source: any, target: any, linkType: LinkType, linkId: string = ''): string => {\r\n if (!source || !target) return '';\r\n \r\n const { pathType } = props.linkConfig;\r\n \r\n switch (linkType) {\r\n case LinkType.PARENT_CHILD:\r\n return generateParentChildPath(source, target, pathType, linkId);\r\n case LinkType.FINISH_TO_START:\r\n return generateFinishToStartPath(source, target, pathType);\r\n case LinkType.START_TO_START:\r\n return generateStartToStartPath(source, target, pathType);\r\n case LinkType.FINISH_TO_FINISH:\r\n return generateFinishToFinishPath(source, target, pathType);\r\n case LinkType.START_TO_FINISH:\r\n return generateStartToFinishPath(source, target, pathType);\r\n default:\r\n return generateFinishToStartPath(source, target, pathType);\r\n }\r\n };\r\n \r\n // 父子关系连线路径(从父任务左边缘到子任务左边缘)\r\n const generateParentChildPath = (parent: any, child: any, pathType: LinkPathType, _linkId: string): string => {\r\n const parentId = parent.id || parent.taskId;\r\n const childId = child.id || child.taskId;\r\n \r\n // 获取子任务在兄弟任务中的索引\r\n const childIndex = getChildIndex(parentId, childId);\r\n \r\n // 起点:父任务左边缘中心\r\n const startX = parent.leftX;\r\n const startY = parent.centerY;\r\n \r\n // 终点:子任务左边缘减去箭头大小(连线终点在箭头底边)\r\n // 箭头尖端在 child.leftX, child.centerY,所以连线终点在箭头底边\r\n const arrowSize = props.linkConfig.arrowSize || 8;\r\n const endX = child.leftX - arrowSize;\r\n const endY = child.centerY; // 直接使用中心Y,与箭头位置一致\r\n \r\n // 根据路径类型生成不同的路径\r\n return generateParentToChildPath(startX, startY, endX, endY, childIndex, pathType);\r\n };\r\n \r\n // 生成从父任务到子任务的连线路径(支持不同路径类型)\r\n const generateParentToChildPath = (\r\n startX: number, startY: number, \r\n endX: number, endY: number, \r\n childIndex: number,\r\n pathType: LinkPathType\r\n ): string => {\r\n const deltaX = endX - startX;\r\n const deltaY = endY - startY;\r\n \r\n // 如果距离很近,直接连线\r\n if (Math.abs(deltaX) < 30 && Math.abs(deltaY) < 15) {\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n \r\n switch (pathType) {\r\n case LinkPathType.STRAIGHT:\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n \r\n case LinkPathType.BEZIER:\r\n return generateBezierPath(startX, startY, endX, endY, childIndex);\r\n \r\n case LinkPathType.RIGHT_ANGLE:\r\n default:\r\n return generateRightAnglePath(startX, startY, endX, endY, childIndex);\r\n }\r\n };\r\n // 生成贝塞尔曲线路径\r\n const generateBezierPath = (\r\n startX: number, startY: number, \r\n endX: number, endY: number, \r\n _childIndex: number\r\n ): string => {\r\n const deltaX = endX - startX;\r\n const deltaY = endY - startY;\r\n const { bezierCurvature } = props.linkConfig;\r\n \r\n // 如果子任务在父任务的右侧(正常情况)\r\n if (deltaX > 0) {\r\n // 使用简单的S曲线,直接连接起点和终点\r\n const curvature = Math.min(Math.abs(deltaX) * bezierCurvature, 60);\r\n const cp1X = startX + curvature;\r\n const cp2X = endX - curvature;\r\n return `M ${startX} ${startY} C ${cp1X} ${startY} ${cp2X} ${endY} ${endX} ${endY}`;\r\n } else {\r\n // 子任务在父任务的左侧,需要绕行\r\n const gap = props.linkConfig.rightAngleOffset;\r\n const bypassY = getSafeBypassY(startY, endY, gap);\r\n const curvature = gap * bezierCurvature;\r\n \r\n return `M ${startX} ${startY} \r\n C ${startX + curvature} ${startY} ${startX + curvature} ${bypassY} ${startX + gap/2} ${bypassY}\r\n C ${endX - curvature} ${bypassY} ${endX - curvature} ${endY} ${endX} ${endY}`;\r\n }\r\n };\r\n \r\n // 生成直角路径\r\n const generateRightAnglePath = (\r\n startX: number, startY: number, \r\n endX: number, endY: number, \r\n childIndex: number\r\n ): string => {\r\n const deltaX = endX - startX;\r\n const { rightAngleOffset, smoothCorners, cornerRadius } = props.linkConfig;\r\n \r\n // 计算该子任务应该对齐的行中心位置\r\n const targetRowCenter = getChildRowCenter(startY, childIndex);\r\n \r\n // 如果子任务在父任务的右侧(正常情况)\r\n if (deltaX > 0) {\r\n const midX = startX + rightAngleOffset;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n // 使用圆角的直角路径 - 简化为L型\r\n const r = Math.min(cornerRadius, Math.abs(endY - startY) / 2, Math.abs(endX - midX) / 2);\r\n return `M ${startX} ${startY}\r\n L ${midX - r} ${startY}\r\n Q ${midX} ${startY} ${midX} ${startY + (endY > startY ? r : -r)}\r\n L ${midX} ${endY - (endY > startY ? r : -r)}\r\n Q ${midX} ${endY} ${midX + r} ${endY}\r\n L ${endX} ${endY}`;\r\n } else {\r\n // 标准直角路径 - 简化为L型\r\n return `M ${startX} ${startY}\r\n L ${midX} ${startY}\r\n L ${midX} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n } else {\r\n // 子任务在父任务的左侧,需要绕行\r\n const gap = rightAngleOffset;\r\n const bypassY = getSafeBypassY(startY, endY, gap);\r\n const midX1 = startX + gap/2;\r\n const midX2 = endX - gap/2;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n const r = Math.min(cornerRadius, gap / 4);\r\n return `M ${startX} ${startY}\r\n L ${midX1 - r} ${startY}\r\n Q ${midX1} ${startY} ${midX1} ${startY + r}\r\n L ${midX1} ${bypassY - r}\r\n Q ${midX1} ${bypassY} ${midX1 + r} ${bypassY}\r\n L ${midX2 - r} ${bypassY}\r\n Q ${midX2} ${bypassY} ${midX2} ${bypassY + r}\r\n L ${midX2} ${endY - r}\r\n Q ${midX2} ${endY} ${midX2 + r} ${endY}\r\n L ${endX} ${endY}`;\r\n } else {\r\n return `M ${startX} ${startY} \r\n L ${midX1} ${startY}\r\n L ${midX1} ${bypassY}\r\n L ${midX2} ${bypassY}\r\n L ${midX2} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n }\r\n };\r\n \r\n // 完成-开始连线路径(支持不同路径类型)\r\n const generateFinishToStartPath = (source: any, target: any, pathType: LinkPathType): string => {\r\n const arrowSize = props.linkConfig.arrowSize || 8;\r\n const startX = source.rightX;\r\n const startY = source.centerY;\r\n // 终点在箭头底边位置\r\n const endX = target.leftX - arrowSize;\r\n const endY = target.centerY;\r\n \r\n return generateConnectionPath(startX, startY, endX, endY, pathType, 'horizontal');\r\n };\r\n \r\n // 开始-开始连线路径(支持不同路径类型)\r\n const generateStartToStartPath = (source: any, target: any, pathType: LinkPathType): string => {\r\n const arrowSize = props.linkConfig.arrowSize || 8;\r\n const startX = source.leftX;\r\n const startY = source.centerY;\r\n // 终点在箭头底边位置\r\n const endX = target.leftX - arrowSize;\r\n const endY = target.centerY;\r\n \r\n return generateConnectionPath(startX, startY, endX, endY, pathType, 'left-u');\r\n };\r\n \r\n // 完成-完成连线路径(支持不同路径类型)\r\n const generateFinishToFinishPath = (source: any, target: any, pathType: LinkPathType): string => {\r\n const arrowSize = props.linkConfig.arrowSize || 8;\r\n const startX = source.rightX;\r\n const startY = source.centerY;\r\n // 终点在箭头底边位置(箭头指向左边)\r\n const endX = target.rightX + arrowSize;\r\n const endY = target.centerY;\r\n \r\n return generateConnectionPath(startX, startY, endX, endY, pathType, 'right-u');\r\n };\r\n \r\n // 开始-完成连线路径(支持不同路径类型)\r\n const generateStartToFinishPath = (source: any, target: any, pathType: LinkPathType): string => {\r\n const arrowSize = props.linkConfig.arrowSize || 8;\r\n const startX = source.leftX;\r\n const startY = source.centerY;\r\n // 终点在箭头底边位置(箭头指向左边)\r\n const endX = target.rightX + arrowSize;\r\n const endY = target.centerY;\r\n \r\n return generateConnectionPath(startX, startY, endX, endY, pathType, 'cross');\r\n };\r\n \r\n // 通用连接路径生成函数\r\n const generateConnectionPath = (\r\n startX: number, startY: number, \r\n endX: number, endY: number, \r\n pathType: LinkPathType, \r\n connectionType: 'horizontal' | 'left-u' | 'right-u' | 'cross'\r\n ): string => {\r\n const { bezierCurvature, rightAngleOffset, smoothCorners, cornerRadius } = props.linkConfig;\r\n \r\n switch (pathType) {\r\n case LinkPathType.STRAIGHT:\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n \r\n case LinkPathType.BEZIER:\r\n return generateBezierConnectionPath(startX, startY, endX, endY, connectionType, bezierCurvature);\r\n \r\n case LinkPathType.RIGHT_ANGLE:\r\n default:\r\n return generateRightAngleConnectionPath(startX, startY, endX, endY, connectionType, rightAngleOffset, smoothCorners, cornerRadius);\r\n }\r\n };\r\n \r\n // 生成贝塞尔连接路径\r\n const generateBezierConnectionPath = (\r\n startX: number, startY: number, \r\n endX: number, endY: number, \r\n connectionType: string,\r\n curvature: number\r\n ): string => {\r\n const deltaX = endX - startX;\r\n const deltaY = endY - startY;\r\n \r\n switch (connectionType) {\r\n case 'horizontal':\r\n // 水平连接(完成-开始)\r\n if (deltaX > 20 && Math.abs(deltaY) < 10) {\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n \r\n if (deltaX < 20) {\r\n // 需要绕行\r\n const gap = 30;\r\n const bypassY = getSafeBypassY(startY, endY, gap);\r\n const curvatureOffset = gap * curvature;\r\n \r\n return `M ${startX} ${startY} \r\n C ${startX + curvatureOffset} ${startY} ${startX + curvatureOffset} ${bypassY} ${startX + gap/2} ${bypassY}\r\n C ${endX - curvatureOffset} ${bypassY} ${endX - curvatureOffset} ${endY} ${endX} ${endY}`;\r\n }\r\n \r\n // 标准S曲线\r\n const curvatureOffset = Math.min(Math.abs(deltaX) * curvature, 60);\r\n const cp1X = startX + curvatureOffset;\r\n const cp2X = endX - curvatureOffset;\r\n \r\n return `M ${startX} ${startY} C ${cp1X} ${startY} ${cp2X} ${endY} ${endX} ${endY}`;\r\n \r\n case 'left-u':\r\n // 左侧U型连接(开始-开始)\r\n const leftGap = 25;\r\n const leftOffsetX = Math.min(startX, endX) - leftGap;\r\n const leftCurvature = leftGap * curvature;\r\n \r\n if (Math.abs(deltaY) > getRowHeight()) {\r\n const midY = alignToGridCenter((startY + endY) / 2);\r\n return `M ${startX} ${startY} \r\n C ${startX - leftCurvature} ${startY} ${leftOffsetX} ${startY + leftCurvature} ${leftOffsetX} ${midY}\r\n C ${leftOffsetX} ${endY - leftCurvature} ${endX - leftCurvature} ${endY} ${endX} ${endY}`;\r\n }\r\n \r\n return `M ${startX} ${startY} C ${leftOffsetX} ${startY} ${leftOffsetX} ${endY} ${endX} ${endY}`;\r\n \r\n case 'right-u':\r\n // 右侧U型连接(完成-完成)\r\n const rightGap = 25;\r\n const rightOffsetX = Math.max(startX, endX) + rightGap;\r\n const rightCurvature = rightGap * curvature;\r\n \r\n if (Math.abs(deltaY) > getRowHeight()) {\r\n const midY = alignToGridCenter((startY + endY) / 2);\r\n return `M ${startX} ${startY} \r\n C ${startX + rightCurvature} ${startY} ${rightOffsetX} ${startY + rightCurvature} ${rightOffsetX} ${midY}\r\n C ${rightOffsetX} ${endY - rightCurvature} ${endX + rightCurvature} ${endY} ${endX} ${endY}`;\r\n }\r\n \r\n return `M ${startX} ${startY} C ${rightOffsetX} ${startY} ${rightOffsetX} ${endY} ${endX} ${endY}`;\r\n \r\n case 'cross':\r\n // 交叉连接(开始-完成)\r\n if (deltaX > 20 && Math.abs(deltaY) < 10) {\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n \r\n const crossCurvature = Math.min(Math.abs(deltaX) * curvature, 60);\r\n const crossCp1X = startX - crossCurvature;\r\n const crossCp2X = endX + crossCurvature;\r\n \r\n return `M ${startX} ${startY} C ${crossCp1X} ${startY} ${crossCp2X} ${endY} ${endX} ${endY}`;\r\n \r\n default:\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n };\r\n \r\n // 生成直角连接路径\r\n const generateRightAngleConnectionPath = (\r\n startX: number, startY: number, \r\n endX: number, endY: number, \r\n connectionType: string,\r\n offset: number,\r\n smoothCorners: boolean,\r\n cornerRadius: number\r\n ): string => {\r\n const deltaX = endX - startX;\r\n const deltaY = endY - startY;\r\n \r\n switch (connectionType) {\r\n case 'horizontal':\r\n // 水平连接(完成-开始)\r\n if (deltaX > 20 && Math.abs(deltaY) < 10) {\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n \r\n if (deltaX < 20) {\r\n // 需要绕行\r\n const gap = offset;\r\n const bypassY = getSafeBypassY(startY, endY, gap);\r\n const midX1 = startX + gap/2;\r\n const midX2 = endX - gap/2;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n const r = Math.min(cornerRadius, gap / 4);\r\n return `M ${startX} ${startY}\r\n L ${midX1 - r} ${startY}\r\n Q ${midX1} ${startY} ${midX1} ${startY + r}\r\n L ${midX1} ${bypassY - r}\r\n Q ${midX1} ${bypassY} ${midX1 + r} ${bypassY}\r\n L ${midX2 - r} ${bypassY}\r\n Q ${midX2} ${bypassY} ${midX2} ${bypassY + r}\r\n L ${midX2} ${endY - r}\r\n Q ${midX2} ${endY} ${midX2 + r} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n \r\n return `M ${startX} ${startY} \r\n L ${midX1} ${startY}\r\n L ${midX1} ${bypassY}\r\n L ${midX2} ${bypassY}\r\n L ${midX2} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n \r\n // 标准L型路径\r\n const midX = startX + Math.abs(deltaX) / 2;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n const r = Math.min(cornerRadius, Math.abs(deltaX) / 4, Math.abs(deltaY) / 2);\r\n if (deltaY > 0) {\r\n return `M ${startX} ${startY}\r\n L ${midX - r} ${startY}\r\n Q ${midX} ${startY} ${midX} ${startY + r}\r\n L ${midX} ${endY - r}\r\n Q ${midX} ${endY} ${midX + r} ${endY}\r\n L ${endX} ${endY}`;\r\n } else {\r\n return `M ${startX} ${startY}\r\n L ${midX - r} ${startY}\r\n Q ${midX} ${startY} ${midX} ${startY - r}\r\n L ${midX} ${endY + r}\r\n Q ${midX} ${endY} ${midX + r} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n }\r\n \r\n return `M ${startX} ${startY} L ${midX} ${startY} L ${midX} ${endY} L ${endX} ${endY}`;\r\n \r\n case 'left-u':\r\n // 左侧U型连接(开始-开始)\r\n const leftGap = offset;\r\n const leftOffsetX = Math.min(startX, endX) - leftGap;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n const r = Math.min(cornerRadius, leftGap / 2, Math.abs(deltaY) / 4);\r\n return `M ${startX} ${startY}\r\n L ${leftOffsetX + r} ${startY}\r\n Q ${leftOffsetX} ${startY} ${leftOffsetX} ${startY + (deltaY > 0 ? r : -r)}\r\n L ${leftOffsetX} ${endY - (deltaY > 0 ? r : -r)}\r\n Q ${leftOffsetX} ${endY} ${leftOffsetX + r} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n \r\n return `M ${startX} ${startY} L ${leftOffsetX} ${startY} L ${leftOffsetX} ${endY} L ${endX} ${endY}`;\r\n \r\n case 'right-u':\r\n // 右侧U型连接(完成-完成)\r\n const rightGap = offset;\r\n const rightOffsetX = Math.max(startX, endX) + rightGap;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n const r = Math.min(cornerRadius, rightGap / 2, Math.abs(deltaY) / 4);\r\n return `M ${startX} ${startY}\r\n L ${rightOffsetX - r} ${startY}\r\n Q ${rightOffsetX} ${startY} ${rightOffsetX} ${startY + (deltaY > 0 ? r : -r)}\r\n L ${rightOffsetX} ${endY - (deltaY > 0 ? r : -r)}\r\n Q ${rightOffsetX} ${endY} ${rightOffsetX - r} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n \r\n return `M ${startX} ${startY} L ${rightOffsetX} ${startY} L ${rightOffsetX} ${endY} L ${endX} ${endY}`;\r\n \r\n case 'cross':\r\n // 交叉连接(开始-完成)\r\n if (deltaX > 20 && Math.abs(deltaY) < 10) {\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n \r\n const crossMidX = startX + deltaX / 2;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n const r = Math.min(cornerRadius, Math.abs(deltaX) / 4, Math.abs(deltaY) / 2);\r\n if (deltaY > 0) {\r\n return `M ${startX} ${startY}\r\n L ${crossMidX - r} ${startY}\r\n Q ${crossMidX} ${startY} ${crossMidX} ${startY + r}\r\n L ${crossMidX} ${endY - r}\r\n Q ${crossMidX} ${endY} ${crossMidX + r} ${endY}\r\n L ${endX} ${endY}`;\r\n } else {\r\n return `M ${startX} ${startY}\r\n L ${crossMidX - r} ${startY}\r\n Q ${crossMidX} ${startY} ${crossMidX} ${startY - r}\r\n L ${crossMidX} ${endY + r}\r\n Q ${crossMidX} ${endY} ${crossMidX + r} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n }\r\n \r\n return `M ${startX} ${startY} L ${crossMidX} ${startY} L ${crossMidX} ${endY} L ${endX} ${endY}`;\r\n \r\n default:\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n };\r\n \r\n // 生成箭头点(直接从子任务位置计算)- 用于父子关系连线\r\n const generateArrowPoints = (childPos: any, arrowSize: number, _linkType: LinkType = LinkType.FINISH_TO_START): string => {\r\n if (!childPos) {\r\n console.warn('子任务位置无效');\r\n return '';\r\n }\r\n \r\n try {\r\n // 箭头尖端:子任务左边缘中心\r\n const tipX = childPos.leftX;\r\n const tipY = childPos.centerY;\r\n \r\n // 父子关系连线:箭头指向右边(指向子任务)\r\n // 箭头底边的两个点\r\n const baseX = tipX - arrowSize;\r\n const baseY1 = tipY - arrowSize / 2;\r\n const baseY2 = tipY + arrowSize / 2;\r\n \r\n const result = `${tipX},${tipY} ${baseX},${baseY1} ${baseX},${baseY2}`;\r\n return result;\r\n } catch (e) {\r\n console.error('箭头生成失败:', e);\r\n return '';\r\n }\r\n };\r\n \r\n // 生成依赖连线的箭头点\r\n const generateDependencyArrowPoints = (\r\n sourcePos: any, \r\n targetPos: any, \r\n linkType: LinkType, \r\n arrowSize: number\r\n ): string => {\r\n if (!sourcePos || !targetPos) return '';\r\n \r\n try {\r\n let endX: number, endY: number;\r\n let direction: 'left' | 'right' | 'up' | 'down' = 'right';\r\n \r\n switch (linkType) {\r\n case LinkType.FINISH_TO_START:\r\n // 箭头指向目标任务的左边缘\r\n endX = targetPos.leftX;\r\n endY = targetPos.centerY;\r\n direction = 'right';\r\n break;\r\n \r\n case LinkType.START_TO_START:\r\n // 箭头指向目标任务的左边缘\r\n endX = targetPos.leftX;\r\n endY = targetPos.centerY;\r\n direction = 'right';\r\n break;\r\n \r\n case LinkType.FINISH_TO_FINISH:\r\n // 箭头指向目标任务的右边缘\r\n endX = targetPos.rightX;\r\n endY = targetPos.centerY;\r\n direction = 'left';\r\n break;\r\n \r\n case LinkType.START_TO_FINISH:\r\n // 箭头指向目标任务的右边缘\r\n endX = targetPos.rightX;\r\n endY = targetPos.centerY;\r\n direction = 'left';\r\n break;\r\n \r\n default:\r\n endX = targetPos.leftX;\r\n endY = targetPos.centerY;\r\n direction = 'right';\r\n }\r\n \r\n // 根据方向生成箭头\r\n let arrowPoint1X: number, arrowPoint1Y: number;\r\n let arrowPoint2X: number, arrowPoint2Y: number;\r\n \r\n if (direction === 'right') {\r\n // 箭头指向右边\r\n arrowPoint1X = endX - arrowSize;\r\n arrowPoint1Y = endY - arrowSize / 2;\r\n arrowPoint2X = endX - arrowSize;\r\n arrowPoint2Y = endY + arrowSize / 2;\r\n } else {\r\n // 箭头指向左边\r\n arrowPoint1X = endX + arrowSize;\r\n arrowPoint1Y = endY - arrowSize / 2;\r\n arrowPoint2X = endX + arrowSize;\r\n arrowPoint2Y = endY + arrowSize / 2;\r\n }\r\n \r\n return `${endX},${endY} ${arrowPoint1X},${arrowPoint1Y} ${arrowPoint2X},${arrowPoint2Y}`;\r\n } catch (e) {\r\n console.error('依赖箭头生成失败:', e);\r\n return '';\r\n }\r\n };\r\n \r\n // 更新连线\r\n // 获取所有被折叠的子任务ID集合(递归)\r\n const getAllCollapsedChildren = (parentId: any): Set<any> => {\r\n const collapsedChildren = new Set<any>();\r\n \r\n const collectChildren = (pid: any) => {\r\n const children = store.tasks.filter(task => task[store.mapFields.parentId] === pid);\r\n children.forEach(child => {\r\n const childId = child[store.mapFields.id];\r\n collapsedChildren.add(childId);\r\n // 递归收集所有子孙任务\r\n collectChildren(childId);\r\n });\r\n };\r\n \r\n collectChildren(parentId);\r\n return collapsedChildren;\r\n };\r\n \r\n // 检查任务是否被折叠(任务本身或其任何祖先被折叠)\r\n const isTaskCollapsed = (taskId: any): boolean => {\r\n // 检查所有已折叠的任务\r\n for (const collapsedId of store.collapsedTasks) {\r\n const collapsedChildren = getAllCollapsedChildren(collapsedId);\r\n if (collapsedChildren.has(taskId)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n };\r\n \r\n const updateLinks = () => {\r\n console.log('updateLinks called');\r\n const newLinks: TaskLink[] = [];\r\n \r\n // 获取连线类型显示配置\r\n const visibility = props.linkConfig.linkTypeVisibility || {\r\n finishToStart: true,\r\n startToStart: true,\r\n finishToFinish: true,\r\n startToFinish: true,\r\n parentChild: true\r\n };\r\n \r\n // 生成父子关系连线(根据 visibility 控制)\r\n if (visibility.parentChild) {\r\n store.tasks.forEach(task => {\r\n const parentId = task[store.mapFields.parentId];\r\n const childId = task[store.mapFields.id];\r\n \r\n if (parentId && parentId !== '0') {\r\n // 检查子任务是否被折叠,如果被折叠则不显示连线\r\n if (isTaskCollapsed(childId)) {\r\n return; // 跳过该连线\r\n }\r\n \r\n const parentPos = getTaskPosition(parentId);\r\n const childPos = getTaskPosition(childId);\r\n \r\n if (parentPos && childPos) {\r\n const linkId = `parent-child-${parentId}-${childId}`;\r\n \r\n // 为位置信息添加任务ID\r\n const parentPosWithId = { ...parentPos, id: parentId, taskId: parentId };\r\n const childPosWithId = { ...childPos, id: childId, taskId: childId };\r\n \r\n const path = generateLinkPath(parentPosWithId, childPosWithId, LinkType.PARENT_CHILD, linkId);\r\n const arrowPoints = props.linkConfig.showArrow ? \r\n generateArrowPoints(childPos, props.linkConfig.arrowSize, LinkType.PARENT_CHILD) : '';\r\n \r\n newLinks.push({\r\n id: linkId,\r\n sourceId: parentId,\r\n targetId: childId,\r\n type: LinkType.PARENT_CHILD,\r\n path,\r\n arrowPoints,\r\n labelX: childPos.centerX,\r\n labelY: childPos.centerY - 10\r\n });\r\n }\r\n }\r\n });\r\n }\r\n \r\n // 生成任务依赖连线\r\n const dependencies = linkDataManager.getDependencies();\r\n console.log('Dependencies:', dependencies);\r\n \r\n // 根据连线类型检查是否显示\r\n const shouldShowLinkType = (linkType: LinkType): boolean => {\r\n switch (linkType) {\r\n case LinkType.FINISH_TO_START:\r\n return visibility.finishToStart;\r\n case LinkType.START_TO_START:\r\n return visibility.startToStart;\r\n case LinkType.FINISH_TO_FINISH:\r\n return visibility.finishToFinish;\r\n case LinkType.START_TO_FINISH:\r\n return visibility.startToFinish;\r\n case LinkType.PARENT_CHILD:\r\n return visibility.parentChild;\r\n default:\r\n return true;\r\n }\r\n };\r\n \r\n dependencies.forEach(dep => {\r\n // 根据 visibility 过滤连线类型\r\n if (!shouldShowLinkType(dep.type)) {\r\n return;\r\n }\r\n \r\n const sourcePos = getTaskPosition(dep.sourceTaskId);\r\n const targetPos = getTaskPosition(dep.targetTaskId);\r\n \r\n if (sourcePos && targetPos) {\r\n const linkId = `dependency-${dep.id}`;\r\n \r\n // 为位置信息添加任务ID\r\n const sourcePosWithId = { ...sourcePos, id: dep.sourceTaskId, taskId: dep.sourceTaskId };\r\n const targetPosWithId = { ...targetPos, id: dep.targetTaskId, taskId: dep.targetTaskId };\r\n \r\n const path = generateLinkPath(sourcePosWithId, targetPosWithId, dep.type, linkId);\r\n \r\n // 根据连线类型生成箭头\r\n const arrowPoints = props.linkConfig.showArrow ? \r\n generateDependencyArrowPoints(sourcePosWithId, targetPosWithId, dep.type, props.linkConfig.arrowSize) : '';\r\n \r\n // 获取连线类型的标签\r\n const labelMap: Record<LinkType, string> = {\r\n [LinkType.FINISH_TO_START]: 'FS',\r\n [LinkType.START_TO_START]: 'SS',\r\n [LinkType.FINISH_TO_FINISH]: 'FF',\r\n [LinkType.START_TO_FINISH]: 'SF',\r\n [LinkType.PARENT_CHILD]: ''\r\n };\r\n \r\n newLinks.push({\r\n id: linkId,\r\n sourceId: dep.sourceTaskId,\r\n targetId: dep.targetTaskId,\r\n type: dep.type,\r\n path,\r\n arrowPoints,\r\n label: labelMap[dep.type] || '',\r\n labelX: (sourcePos.centerX + targetPos.centerX) / 2,\r\n labelY: (sourcePos.centerY + targetPos.centerY) / 2 - 10\r\n });\r\n }\r\n });\r\n \r\n links.value = newLinks;\r\n };\r\n \r\n // 监听任务变化\r\n watch(() => store.tasks, () => {\r\n // 延迟更新,确保DOM已更新\r\n setTimeout(updateLinks, 50);\r\n }, { deep: true });\r\n \r\n watch(() => store.scale, () => {\r\n setTimeout(updateLinks, 50);\r\n });\r\n \r\n // 监听模式变化(月、日、时切换)\r\n watch(() => store.mode, () => {\r\n // 模式切换时需要重新计算bar位置,延迟更新确保bar重绘完成\r\n setTimeout(updateLinks, 200);\r\n });\r\n \r\n // 监听时间轴变化\r\n watch(() => store.timelineCellCount, () => {\r\n setTimeout(updateLinks, 100);\r\n });\r\n \r\n // 监听甘特图日期范围变化\r\n watch(() => [store.startGanttDate, store.endGanttDate], () => {\r\n setTimeout(updateLinks, 100);\r\n });\r\n \r\n // 监听barDate变化(拖拽和调整大小时会触发)\r\n watch(() => store.barDate, () => {\r\n requestAnimationFrame(updateLinks);\r\n }, { deep: true });\r\n \r\n // 监听连线类型显示配置变化\r\n watch(() => props.linkConfig.linkTypeVisibility, () => {\r\n setTimeout(updateLinks, 50);\r\n }, { deep: true });\r\n \r\n // 监听折叠状态变化\r\n watch(() => store.collapsedTasks, () => {\r\n setTimeout(updateLinks, 50);\r\n }, { deep: true });\r\n \r\n // 监听DOM变化(拖拽时重绘)\r\n let resizeObserver: ResizeObserver | null = null;\r\n let mutationObserver: MutationObserver | null = null;\r\n \r\n onMounted(() => {\r\n // 初始更新\r\n setTimeout(updateLinks, 100);\r\n \r\n // 监听容器大小变化\r\n const container = document.querySelector('.gantt');\r\n if (container) {\r\n resizeObserver = new ResizeObserver(updateLinks);\r\n resizeObserver.observe(container);\r\n \r\n\r\n \r\n // 监听DOM变化(任务条位置变化)\r\n mutationObserver = new MutationObserver((mutations) => {\r\n let shouldUpdate = false;\r\n mutations.forEach(mutation => {\r\n const target = mutation.target as HTMLElement;\r\n // 检查是否是SVG bar元素或其父元素\r\n if (mutation.type === 'attributes') {\r\n const attrName = mutation.attributeName;\r\n if (attrName === 'data-x' || \r\n attrName === 'width' || \r\n attrName === 'style' ||\r\n attrName === 'transform') {\r\n // 检查是否是bar元素\r\n if (target.classList?.contains('bar') || \r\n target.classList?.contains('barRow') ||\r\n target.tagName === 'svg') {\r\n shouldUpdate = true;\r\n }\r\n }\r\n }\r\n });\r\n if (shouldUpdate) {\r\n requestAnimationFrame(updateLinks);\r\n }\r\n });\r\n \r\n mutationObserver.observe(container, {\r\n attributes: true,\r\n subtree: true,\r\n attributeFilter: ['style', 'data-x', 'transform', 'width']\r\n });\r\n }\r\n });\r\n \r\n onUnmounted(() => {\r\n if (resizeObserver) {\r\n resizeObserver.disconnect();\r\n }\r\n if (mutationObserver) {\r\n mutationObserver.disconnect();\r\n }\r\n });\r\n \r\n // 虚线动画相关方法\r\n \r\n // 判断是否为虚线\r\n const isDashedLine = (linkType: LinkType): boolean => {\r\n const style = getLinkStyle(linkType);\r\n return !!(style.dashArray && style.dashArray.length > 0);\r\n };\r\n \r\n // 获取虚线流动动画样式\r\n const getDashAnimationStyle = (link: TaskLink) => {\r\n if (!isDashedLine(link.type) || !props.linkConfig.enableDashAnimation) {\r\n return {};\r\n }\r\n \r\n const style = getLinkStyle(link.type);\r\n const speed = props.linkConfig.dashAnimationSpeed || 0.8; // 更快的默认速度\r\n \r\n // 父子关系连线稍快一些\r\n const adjustedSpeed = link.type === LinkType.PARENT_CHILD ? speed * 0.6 : speed;\r\n \r\n // 计算虚线总长度用于动画\r\n const dashArray = style.dashArray || '5,5';\r\n const dashParts = dashArray.split(',').map(Number);\r\n const dashLength = dashParts.reduce((sum, part) => sum + part, 0);\r\n \r\n return {\r\n '--animation-duration': `${adjustedSpeed}s`,\r\n '--dash-length': `${dashLength}px`\r\n };\r\n };\r\n \r\n return {\r\n links,\r\n updateLinks,\r\n getLinkStyle,\r\n isDashedLine,\r\n getDashAnimationStyle\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.task-links-layer {\r\n pointer-events: none;\r\n \r\n .task-link {\r\n transition: stroke-width 0.2s ease;\r\n \r\n &:hover {\r\n stroke-width: 3;\r\n }\r\n \r\n &.parent-child {\r\n opacity: 0.7;\r\n }\r\n \r\n &.finish-to-start {\r\n opacity: 0.9;\r\n }\r\n \r\n // 虚线流动动画\r\n &.dash-animated {\r\n animation: dashFlow var(--animation-duration, 3s) linear infinite;\r\n }\r\n }\r\n \r\n // 虚线流动动画关键帧\r\n @keyframes dashFlow {\r\n 0% {\r\n stroke-dashoffset: 0;\r\n }\r\n 100% {\r\n stroke-dashoffset: calc(-1 * var(--dash-length, 20px));\r\n }\r\n }\r\n \r\n .task-link-arrow {\r\n transition: fill 0.2s ease;\r\n opacity: 1;\r\n pointer-events: none;\r\n }\r\n \r\n .task-link-label {\r\n font-family: Arial, sans-serif;\r\n user-select: none;\r\n }\r\n}\r\n</style>","<template>\r\n <svg \r\n class=\"task-links-layer\" \r\n :width=\"containerWidth\" \r\n :height=\"containerHeight\"\r\n :style=\"{ \r\n position: 'absolute', \r\n top: '0px', \r\n left: '0px', \r\n pointerEvents: 'none',\r\n zIndex: 50,\r\n overflow: 'visible'\r\n }\"\r\n >\r\n <!-- 渲染所有连线 -->\r\n <g v-for=\"link in links\" :key=\"link.id\">\r\n <!-- 连线路径 -->\r\n <path\r\n :d=\"link.path\"\r\n :stroke=\"getLinkStyle(link.type).color\"\r\n :stroke-width=\"getLinkStyle(link.type).width\"\r\n :stroke-dasharray=\"getLinkStyle(link.type).dashArray\"\r\n fill=\"none\"\r\n :class=\"['task-link', link.type, { \r\n 'dash-animated': isDashedLine(link.type) && linkConfig.enableDashAnimation \r\n }]\"\r\n :style=\"getDashAnimationStyle(link)\"\r\n />\r\n \r\n <!-- 箭头 -->\r\n <polygon\r\n v-if=\"linkConfig.showArrow && link.arrowPoints && link.arrowPoints.length > 0\"\r\n :points=\"link.arrowPoints\"\r\n :fill=\"getLinkStyle(link.type).color\"\r\n :stroke=\"getLinkStyle(link.type).color\"\r\n :stroke-width=\"0.5\"\r\n class=\"task-link-arrow\"\r\n />\r\n \r\n <!-- 连线标签(可选) -->\r\n <text\r\n v-if=\"link.label && linkConfig.showLabels\"\r\n :x=\"link.labelX\"\r\n :y=\"link.labelY\"\r\n :fill=\"linkConfig.labelColor\"\r\n :font-size=\"linkConfig.labelFontSize\"\r\n text-anchor=\"middle\"\r\n class=\"task-link-label\"\r\n >\r\n {{ link.label }}\r\n </text>\r\n </g>\r\n </svg>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, watch, ref, onMounted, onUnmounted } from 'vue';\r\nimport { store } from './Store';\r\nimport { LinkType, LinkPathType, type LinkConfig, type TaskLink } from './Types';\r\nimport { linkDataManager } from './LinkConfig';\r\n\r\n\r\n\r\nexport default defineComponent({\r\n name: 'TaskLinks',\r\n props: {\r\n containerWidth: {\r\n type: Number,\r\n required: true\r\n },\r\n containerHeight: {\r\n type: Number,\r\n required: true\r\n },\r\n linkConfig: {\r\n type: Object as () => LinkConfig,\r\n default: () => ({\r\n color: '#3498db',\r\n width: 2,\r\n dashArray: undefined,\r\n showArrow: true,\r\n arrowColor: undefined,\r\n arrowSize: 8,\r\n showLabels: false,\r\n labelColor: '#666',\r\n labelFontSize: 12,\r\n cornerRadius: 5,\r\n pathType: LinkPathType.BEZIER,\r\n bezierCurvature: 0.4,\r\n rightAngleOffset: 30,\r\n smoothCorners: true,\r\n enableDashAnimation: true,\r\n dashAnimationSpeed: 0.8,\r\n parentChildStyle: {\r\n color: '#95a5a6',\r\n width: 1,\r\n dashArray: '3,3'\r\n }\r\n })\r\n }\r\n },\r\n setup(props) {\r\n const links = ref<TaskLink[]>([]);\r\n \r\n // 获取任务位置信息\r\n const getTaskPosition = (taskId: string) => {\r\n const task = store.tasks.find(t => t[store.mapFields.id] === taskId);\r\n if (!task) return null;\r\n \r\n // 查找对应的DOM元素 - 查找barRow而不是bar\r\n const barElement = document.querySelector(`[data-task-id=\"${taskId}\"]`) as HTMLElement;\r\n if (!barElement) return null;\r\n \r\n // 查找SVG bar元素获取实际位置\r\n const svgBar = barElement.querySelector('.bar') as SVGSVGElement;\r\n if (!svgBar) return null;\r\n \r\n // 获取容器 - 使用更精确的选择器\r\n const rightTableContent = barElement.closest('.table .content') as HTMLElement;\r\n if (!rightTableContent) return null;\r\n \r\n // 获取SVG的transform属性\r\n const dataX = parseFloat(svgBar.getAttribute('data-x') || '0');\r\n const barWidth = svgBar.width.baseVal.value;\r\n \r\n // 获取任务在当前容器中的位置\r\n const barRowRect = barElement.getBoundingClientRect();\r\n const containerRect = rightTableContent.getBoundingClientRect();\r\n \r\n // 计算任务相对于容器的位置\r\n const relativeY = barRowRect.top - containerRect.top;\r\n \r\n return {\r\n x: dataX,\r\n y: relativeY,\r\n width: barWidth,\r\n height: barRowRect.height,\r\n centerX: dataX + barWidth / 2,\r\n centerY: relativeY + barRowRect.height / 2,\r\n rightX: dataX + barWidth,\r\n leftX: dataX,\r\n topY: relativeY,\r\n bottomY: relativeY + barRowRect.height\r\n };\r\n };\r\n \r\n // 获取父任务的子任务索引(用于计算偏移)\r\n const getChildIndex = (parentId: string, childId: string): number => {\r\n const siblings = store.tasks.filter(task => \r\n task[store.mapFields.parentId] === parentId && \r\n task[store.mapFields.parentId] !== '0'\r\n );\r\n return siblings.findIndex(task => task[store.mapFields.id] === childId);\r\n };\r\n \r\n\r\n \r\n // 获取行高(从DOM或props中获取)\r\n const getRowHeight = (): number => {\r\n // 尝试从第一个barRow元素获取高度\r\n const firstBarRow = document.querySelector('.barRow') as HTMLElement;\r\n if (firstBarRow) {\r\n return firstBarRow.offsetHeight;\r\n }\r\n // 默认行高\r\n return 50;\r\n };\r\n \r\n // 将Y坐标对齐到最近的单元格中心线\r\n const alignToGridCenter = (y: number): number => {\r\n const rowHeight = getRowHeight();\r\n const rowIndex = Math.floor(y / rowHeight);\r\n return rowIndex * rowHeight + rowHeight / 2;\r\n };\r\n \r\n // 根据子任务索引获取对应行的中心线位置(只能向下)\r\n const getChildRowCenter = (parentY: number, childIndex: number): number => {\r\n const rowHeight = getRowHeight();\r\n // 从父任务下方开始,第一个子任务在第一行,第二个在第二行,以此类推\r\n const parentRowIndex = Math.floor(parentY / rowHeight);\r\n const targetRowIndex = parentRowIndex + 1 + childIndex;\r\n return targetRowIndex * rowHeight + rowHeight / 2;\r\n };\r\n \r\n // 确保绕行路径只能向下(不能高过父任务)\r\n const getSafeBypassY = (parentY: number, childY: number, gap: number): number => {\r\n const rowHeight = getRowHeight();\r\n const parentBottomY = Math.floor(parentY / rowHeight) * rowHeight + rowHeight;\r\n const minBypassY = parentBottomY + gap;\r\n \r\n // 如果子任务在父任务下方很远,可以选择一个中间位置\r\n if (childY > minBypassY + rowHeight) {\r\n return alignToGridCenter(minBypassY + rowHeight / 2);\r\n }\r\n \r\n return alignToGridCenter(minBypassY);\r\n };\r\n \r\n // 获取连线类型对应的颜色(从配置中读取)\r\n const getLinkTypeColor = (linkType: LinkType): string => {\r\n const colors = props.linkConfig.linkTypeColors;\r\n if (!colors) {\r\n // 默认颜色\r\n const defaultColors: Record<LinkType, string> = {\r\n [LinkType.FINISH_TO_START]: '#3498db',\r\n [LinkType.START_TO_START]: '#2ecc71',\r\n [LinkType.FINISH_TO_FINISH]: '#e74c3c',\r\n [LinkType.START_TO_FINISH]: '#f39c12',\r\n [LinkType.PARENT_CHILD]: '#95a5a6'\r\n };\r\n return defaultColors[linkType] || props.linkConfig.color;\r\n }\r\n \r\n switch (linkType) {\r\n case LinkType.FINISH_TO_START:\r\n return colors.finishToStart || '#3498db';\r\n case LinkType.START_TO_START:\r\n return colors.startToStart || '#2ecc71';\r\n case LinkType.FINISH_TO_FINISH:\r\n return colors.finishToFinish || '#e74c3c';\r\n case LinkType.START_TO_FINISH:\r\n return colors.startToFinish || '#f39c12';\r\n default:\r\n return props.linkConfig.color;\r\n }\r\n };\r\n\r\n // 获取特定连线类型的样式\r\n const getLinkStyle = (linkType: LinkType) => {\r\n if (linkType === LinkType.PARENT_CHILD) {\r\n return {\r\n color: props.linkConfig.parentChildStyle.color || '#95a5a6',\r\n width: props.linkConfig.parentChildStyle.width,\r\n dashArray: props.linkConfig.parentChildStyle.dashArray\r\n };\r\n }\r\n \r\n // 任务依赖连线使用对应类型的颜色\r\n return {\r\n color: getLinkTypeColor(linkType),\r\n width: props.linkConfig.width,\r\n dashArray: props.linkConfig.dashArray\r\n };\r\n };\r\n \r\n // 生成连线路径\r\n const generateLinkPath = (source: any, target: any, linkType: LinkType, linkId: string = ''): string => {\r\n if (!source || !target) return '';\r\n \r\n const { pathType } = props.linkConfig;\r\n \r\n switch (linkType) {\r\n case LinkType.PARENT_CHILD:\r\n return generateParentChildPath(source, target, pathType, linkId);\r\n case LinkType.FINISH_TO_START:\r\n return generateFinishToStartPath(source, target, pathType);\r\n case LinkType.START_TO_START:\r\n return generateStartToStartPath(source, target, pathType);\r\n case LinkType.FINISH_TO_FINISH:\r\n return generateFinishToFinishPath(source, target, pathType);\r\n case LinkType.START_TO_FINISH:\r\n return generateStartToFinishPath(source, target, pathType);\r\n default:\r\n return generateFinishToStartPath(source, target, pathType);\r\n }\r\n };\r\n \r\n // 父子关系连线路径(从父任务左边缘到子任务左边缘)\r\n const generateParentChildPath = (parent: any, child: any, pathType: LinkPathType, _linkId: string): string => {\r\n const parentId = parent.id || parent.taskId;\r\n const childId = child.id || child.taskId;\r\n \r\n // 获取子任务在兄弟任务中的索引\r\n const childIndex = getChildIndex(parentId, childId);\r\n \r\n // 起点:父任务左边缘中心\r\n const startX = parent.leftX;\r\n const startY = parent.centerY;\r\n \r\n // 终点:子任务左边缘减去箭头大小(连线终点在箭头底边)\r\n // 箭头尖端在 child.leftX, child.centerY,所以连线终点在箭头底边\r\n const arrowSize = props.linkConfig.arrowSize || 8;\r\n const endX = child.leftX - arrowSize;\r\n const endY = child.centerY; // 直接使用中心Y,与箭头位置一致\r\n \r\n // 根据路径类型生成不同的路径\r\n return generateParentToChildPath(startX, startY, endX, endY, childIndex, pathType);\r\n };\r\n \r\n // 生成从父任务到子任务的连线路径(支持不同路径类型)\r\n const generateParentToChildPath = (\r\n startX: number, startY: number, \r\n endX: number, endY: number, \r\n childIndex: number,\r\n pathType: LinkPathType\r\n ): string => {\r\n const deltaX = endX - startX;\r\n const deltaY = endY - startY;\r\n \r\n // 如果距离很近,直接连线\r\n if (Math.abs(deltaX) < 30 && Math.abs(deltaY) < 15) {\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n \r\n switch (pathType) {\r\n case LinkPathType.STRAIGHT:\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n \r\n case LinkPathType.BEZIER:\r\n return generateBezierPath(startX, startY, endX, endY, childIndex);\r\n \r\n case LinkPathType.RIGHT_ANGLE:\r\n default:\r\n return generateRightAnglePath(startX, startY, endX, endY, childIndex);\r\n }\r\n };\r\n // 生成贝塞尔曲线路径\r\n const generateBezierPath = (\r\n startX: number, startY: number, \r\n endX: number, endY: number, \r\n _childIndex: number\r\n ): string => {\r\n const deltaX = endX - startX;\r\n const deltaY = endY - startY;\r\n const { bezierCurvature } = props.linkConfig;\r\n \r\n // 如果子任务在父任务的右侧(正常情况)\r\n if (deltaX > 0) {\r\n // 使用简单的S曲线,直接连接起点和终点\r\n const curvature = Math.min(Math.abs(deltaX) * bezierCurvature, 60);\r\n const cp1X = startX + curvature;\r\n const cp2X = endX - curvature;\r\n return `M ${startX} ${startY} C ${cp1X} ${startY} ${cp2X} ${endY} ${endX} ${endY}`;\r\n } else {\r\n // 子任务在父任务的左侧,需要绕行\r\n const gap = props.linkConfig.rightAngleOffset;\r\n const bypassY = getSafeBypassY(startY, endY, gap);\r\n const curvature = gap * bezierCurvature;\r\n \r\n return `M ${startX} ${startY} \r\n C ${startX + curvature} ${startY} ${startX + curvature} ${bypassY} ${startX + gap/2} ${bypassY}\r\n C ${endX - curvature} ${bypassY} ${endX - curvature} ${endY} ${endX} ${endY}`;\r\n }\r\n };\r\n \r\n // 生成直角路径\r\n const generateRightAnglePath = (\r\n startX: number, startY: number, \r\n endX: number, endY: number, \r\n childIndex: number\r\n ): string => {\r\n const deltaX = endX - startX;\r\n const { rightAngleOffset, smoothCorners, cornerRadius } = props.linkConfig;\r\n \r\n // 计算该子任务应该对齐的行中心位置\r\n const targetRowCenter = getChildRowCenter(startY, childIndex);\r\n \r\n // 如果子任务在父任务的右侧(正常情况)\r\n if (deltaX > 0) {\r\n const midX = startX + rightAngleOffset;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n // 使用圆角的直角路径 - 简化为L型\r\n const r = Math.min(cornerRadius, Math.abs(endY - startY) / 2, Math.abs(endX - midX) / 2);\r\n return `M ${startX} ${startY}\r\n L ${midX - r} ${startY}\r\n Q ${midX} ${startY} ${midX} ${startY + (endY > startY ? r : -r)}\r\n L ${midX} ${endY - (endY > startY ? r : -r)}\r\n Q ${midX} ${endY} ${midX + r} ${endY}\r\n L ${endX} ${endY}`;\r\n } else {\r\n // 标准直角路径 - 简化为L型\r\n return `M ${startX} ${startY}\r\n L ${midX} ${startY}\r\n L ${midX} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n } else {\r\n // 子任务在父任务的左侧,需要绕行\r\n const gap = rightAngleOffset;\r\n const bypassY = getSafeBypassY(startY, endY, gap);\r\n const midX1 = startX + gap/2;\r\n const midX2 = endX - gap/2;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n const r = Math.min(cornerRadius, gap / 4);\r\n return `M ${startX} ${startY}\r\n L ${midX1 - r} ${startY}\r\n Q ${midX1} ${startY} ${midX1} ${startY + r}\r\n L ${midX1} ${bypassY - r}\r\n Q ${midX1} ${bypassY} ${midX1 + r} ${bypassY}\r\n L ${midX2 - r} ${bypassY}\r\n Q ${midX2} ${bypassY} ${midX2} ${bypassY + r}\r\n L ${midX2} ${endY - r}\r\n Q ${midX2} ${endY} ${midX2 + r} ${endY}\r\n L ${endX} ${endY}`;\r\n } else {\r\n return `M ${startX} ${startY} \r\n L ${midX1} ${startY}\r\n L ${midX1} ${bypassY}\r\n L ${midX2} ${bypassY}\r\n L ${midX2} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n }\r\n };\r\n \r\n // 完成-开始连线路径(支持不同路径类型)\r\n const generateFinishToStartPath = (source: any, target: any, pathType: LinkPathType): string => {\r\n const arrowSize = props.linkConfig.arrowSize || 8;\r\n const startX = source.rightX;\r\n const startY = source.centerY;\r\n // 终点在箭头底边位置\r\n const endX = target.leftX - arrowSize;\r\n const endY = target.centerY;\r\n \r\n return generateConnectionPath(startX, startY, endX, endY, pathType, 'horizontal');\r\n };\r\n \r\n // 开始-开始连线路径(支持不同路径类型)\r\n const generateStartToStartPath = (source: any, target: any, pathType: LinkPathType): string => {\r\n const arrowSize = props.linkConfig.arrowSize || 8;\r\n const startX = source.leftX;\r\n const startY = source.centerY;\r\n // 终点在箭头底边位置\r\n const endX = target.leftX - arrowSize;\r\n const endY = target.centerY;\r\n \r\n return generateConnectionPath(startX, startY, endX, endY, pathType, 'left-u');\r\n };\r\n \r\n // 完成-完成连线路径(支持不同路径类型)\r\n const generateFinishToFinishPath = (source: any, target: any, pathType: LinkPathType): string => {\r\n const arrowSize = props.linkConfig.arrowSize || 8;\r\n const startX = source.rightX;\r\n const startY = source.centerY;\r\n // 终点在箭头底边位置(箭头指向左边)\r\n const endX = target.rightX + arrowSize;\r\n const endY = target.centerY;\r\n \r\n return generateConnectionPath(startX, startY, endX, endY, pathType, 'right-u');\r\n };\r\n \r\n // 开始-完成连线路径(支持不同路径类型)\r\n const generateStartToFinishPath = (source: any, target: any, pathType: LinkPathType): string => {\r\n const arrowSize = props.linkConfig.arrowSize || 8;\r\n const startX = source.leftX;\r\n const startY = source.centerY;\r\n // 终点在箭头底边位置(箭头指向左边)\r\n const endX = target.rightX + arrowSize;\r\n const endY = target.centerY;\r\n \r\n return generateConnectionPath(startX, startY, endX, endY, pathType, 'cross');\r\n };\r\n \r\n // 通用连接路径生成函数\r\n const generateConnectionPath = (\r\n startX: number, startY: number, \r\n endX: number, endY: number, \r\n pathType: LinkPathType, \r\n connectionType: 'horizontal' | 'left-u' | 'right-u' | 'cross'\r\n ): string => {\r\n const { bezierCurvature, rightAngleOffset, smoothCorners, cornerRadius } = props.linkConfig;\r\n \r\n switch (pathType) {\r\n case LinkPathType.STRAIGHT:\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n \r\n case LinkPathType.BEZIER:\r\n return generateBezierConnectionPath(startX, startY, endX, endY, connectionType, bezierCurvature);\r\n \r\n case LinkPathType.RIGHT_ANGLE:\r\n default:\r\n return generateRightAngleConnectionPath(startX, startY, endX, endY, connectionType, rightAngleOffset, smoothCorners, cornerRadius);\r\n }\r\n };\r\n \r\n // 生成贝塞尔连接路径\r\n const generateBezierConnectionPath = (\r\n startX: number, startY: number, \r\n endX: number, endY: number, \r\n connectionType: string,\r\n curvature: number\r\n ): string => {\r\n const deltaX = endX - startX;\r\n const deltaY = endY - startY;\r\n \r\n switch (connectionType) {\r\n case 'horizontal':\r\n // 水平连接(完成-开始)\r\n if (deltaX > 20 && Math.abs(deltaY) < 10) {\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n \r\n if (deltaX < 20) {\r\n // 需要绕行\r\n const gap = 30;\r\n const bypassY = getSafeBypassY(startY, endY, gap);\r\n const curvatureOffset = gap * curvature;\r\n \r\n return `M ${startX} ${startY} \r\n C ${startX + curvatureOffset} ${startY} ${startX + curvatureOffset} ${bypassY} ${startX + gap/2} ${bypassY}\r\n C ${endX - curvatureOffset} ${bypassY} ${endX - curvatureOffset} ${endY} ${endX} ${endY}`;\r\n }\r\n \r\n // 标准S曲线\r\n const curvatureOffset = Math.min(Math.abs(deltaX) * curvature, 60);\r\n const cp1X = startX + curvatureOffset;\r\n const cp2X = endX - curvatureOffset;\r\n \r\n return `M ${startX} ${startY} C ${cp1X} ${startY} ${cp2X} ${endY} ${endX} ${endY}`;\r\n \r\n case 'left-u':\r\n // 左侧U型连接(开始-开始)\r\n const leftGap = 25;\r\n const leftOffsetX = Math.min(startX, endX) - leftGap;\r\n const leftCurvature = leftGap * curvature;\r\n \r\n if (Math.abs(deltaY) > getRowHeight()) {\r\n const midY = alignToGridCenter((startY + endY) / 2);\r\n return `M ${startX} ${startY} \r\n C ${startX - leftCurvature} ${startY} ${leftOffsetX} ${startY + leftCurvature} ${leftOffsetX} ${midY}\r\n C ${leftOffsetX} ${endY - leftCurvature} ${endX - leftCurvature} ${endY} ${endX} ${endY}`;\r\n }\r\n \r\n return `M ${startX} ${startY} C ${leftOffsetX} ${startY} ${leftOffsetX} ${endY} ${endX} ${endY}`;\r\n \r\n case 'right-u':\r\n // 右侧U型连接(完成-完成)\r\n const rightGap = 25;\r\n const rightOffsetX = Math.max(startX, endX) + rightGap;\r\n const rightCurvature = rightGap * curvature;\r\n \r\n if (Math.abs(deltaY) > getRowHeight()) {\r\n const midY = alignToGridCenter((startY + endY) / 2);\r\n return `M ${startX} ${startY} \r\n C ${startX + rightCurvature} ${startY} ${rightOffsetX} ${startY + rightCurvature} ${rightOffsetX} ${midY}\r\n C ${rightOffsetX} ${endY - rightCurvature} ${endX + rightCurvature} ${endY} ${endX} ${endY}`;\r\n }\r\n \r\n return `M ${startX} ${startY} C ${rightOffsetX} ${startY} ${rightOffsetX} ${endY} ${endX} ${endY}`;\r\n \r\n case 'cross':\r\n // 交叉连接(开始-完成)\r\n if (deltaX > 20 && Math.abs(deltaY) < 10) {\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n \r\n const crossCurvature = Math.min(Math.abs(deltaX) * curvature, 60);\r\n const crossCp1X = startX - crossCurvature;\r\n const crossCp2X = endX + crossCurvature;\r\n \r\n return `M ${startX} ${startY} C ${crossCp1X} ${startY} ${crossCp2X} ${endY} ${endX} ${endY}`;\r\n \r\n default:\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n };\r\n \r\n // 生成直角连接路径\r\n const generateRightAngleConnectionPath = (\r\n startX: number, startY: number, \r\n endX: number, endY: number, \r\n connectionType: string,\r\n offset: number,\r\n smoothCorners: boolean,\r\n cornerRadius: number\r\n ): string => {\r\n const deltaX = endX - startX;\r\n const deltaY = endY - startY;\r\n \r\n switch (connectionType) {\r\n case 'horizontal':\r\n // 水平连接(完成-开始)\r\n if (deltaX > 20 && Math.abs(deltaY) < 10) {\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n \r\n if (deltaX < 20) {\r\n // 需要绕行\r\n const gap = offset;\r\n const bypassY = getSafeBypassY(startY, endY, gap);\r\n const midX1 = startX + gap/2;\r\n const midX2 = endX - gap/2;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n const r = Math.min(cornerRadius, gap / 4);\r\n return `M ${startX} ${startY}\r\n L ${midX1 - r} ${startY}\r\n Q ${midX1} ${startY} ${midX1} ${startY + r}\r\n L ${midX1} ${bypassY - r}\r\n Q ${midX1} ${bypassY} ${midX1 + r} ${bypassY}\r\n L ${midX2 - r} ${bypassY}\r\n Q ${midX2} ${bypassY} ${midX2} ${bypassY + r}\r\n L ${midX2} ${endY - r}\r\n Q ${midX2} ${endY} ${midX2 + r} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n \r\n return `M ${startX} ${startY} \r\n L ${midX1} ${startY}\r\n L ${midX1} ${bypassY}\r\n L ${midX2} ${bypassY}\r\n L ${midX2} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n \r\n // 标准L型路径\r\n const midX = startX + Math.abs(deltaX) / 2;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n const r = Math.min(cornerRadius, Math.abs(deltaX) / 4, Math.abs(deltaY) / 2);\r\n if (deltaY > 0) {\r\n return `M ${startX} ${startY}\r\n L ${midX - r} ${startY}\r\n Q ${midX} ${startY} ${midX} ${startY + r}\r\n L ${midX} ${endY - r}\r\n Q ${midX} ${endY} ${midX + r} ${endY}\r\n L ${endX} ${endY}`;\r\n } else {\r\n return `M ${startX} ${startY}\r\n L ${midX - r} ${startY}\r\n Q ${midX} ${startY} ${midX} ${startY - r}\r\n L ${midX} ${endY + r}\r\n Q ${midX} ${endY} ${midX + r} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n }\r\n \r\n return `M ${startX} ${startY} L ${midX} ${startY} L ${midX} ${endY} L ${endX} ${endY}`;\r\n \r\n case 'left-u':\r\n // 左侧U型连接(开始-开始)\r\n const leftGap = offset;\r\n const leftOffsetX = Math.min(startX, endX) - leftGap;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n const r = Math.min(cornerRadius, leftGap / 2, Math.abs(deltaY) / 4);\r\n return `M ${startX} ${startY}\r\n L ${leftOffsetX + r} ${startY}\r\n Q ${leftOffsetX} ${startY} ${leftOffsetX} ${startY + (deltaY > 0 ? r : -r)}\r\n L ${leftOffsetX} ${endY - (deltaY > 0 ? r : -r)}\r\n Q ${leftOffsetX} ${endY} ${leftOffsetX + r} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n \r\n return `M ${startX} ${startY} L ${leftOffsetX} ${startY} L ${leftOffsetX} ${endY} L ${endX} ${endY}`;\r\n \r\n case 'right-u':\r\n // 右侧U型连接(完成-完成)\r\n const rightGap = offset;\r\n const rightOffsetX = Math.max(startX, endX) + rightGap;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n const r = Math.min(cornerRadius, rightGap / 2, Math.abs(deltaY) / 4);\r\n return `M ${startX} ${startY}\r\n L ${rightOffsetX - r} ${startY}\r\n Q ${rightOffsetX} ${startY} ${rightOffsetX} ${startY + (deltaY > 0 ? r : -r)}\r\n L ${rightOffsetX} ${endY - (deltaY > 0 ? r : -r)}\r\n Q ${rightOffsetX} ${endY} ${rightOffsetX - r} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n \r\n return `M ${startX} ${startY} L ${rightOffsetX} ${startY} L ${rightOffsetX} ${endY} L ${endX} ${endY}`;\r\n \r\n case 'cross':\r\n // 交叉连接(开始-完成)\r\n if (deltaX > 20 && Math.abs(deltaY) < 10) {\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n \r\n const crossMidX = startX + deltaX / 2;\r\n \r\n if (smoothCorners && cornerRadius > 0) {\r\n const r = Math.min(cornerRadius, Math.abs(deltaX) / 4, Math.abs(deltaY) / 2);\r\n if (deltaY > 0) {\r\n return `M ${startX} ${startY}\r\n L ${crossMidX - r} ${startY}\r\n Q ${crossMidX} ${startY} ${crossMidX} ${startY + r}\r\n L ${crossMidX} ${endY - r}\r\n Q ${crossMidX} ${endY} ${crossMidX + r} ${endY}\r\n L ${endX} ${endY}`;\r\n } else {\r\n return `M ${startX} ${startY}\r\n L ${crossMidX - r} ${startY}\r\n Q ${crossMidX} ${startY} ${crossMidX} ${startY - r}\r\n L ${crossMidX} ${endY + r}\r\n Q ${crossMidX} ${endY} ${crossMidX + r} ${endY}\r\n L ${endX} ${endY}`;\r\n }\r\n }\r\n \r\n return `M ${startX} ${startY} L ${crossMidX} ${startY} L ${crossMidX} ${endY} L ${endX} ${endY}`;\r\n \r\n default:\r\n return `M ${startX} ${startY} L ${endX} ${endY}`;\r\n }\r\n };\r\n \r\n // 生成箭头点(直接从子任务位置计算)- 用于父子关系连线\r\n const generateArrowPoints = (childPos: any, arrowSize: number, _linkType: LinkType = LinkType.FINISH_TO_START): string => {\r\n if (!childPos) {\r\n console.warn('子任务位置无效');\r\n return '';\r\n }\r\n \r\n try {\r\n // 箭头尖端:子任务左边缘中心\r\n const tipX = childPos.leftX;\r\n const tipY = childPos.centerY;\r\n \r\n // 父子关系连线:箭头指向右边(指向子任务)\r\n // 箭头底边的两个点\r\n const baseX = tipX - arrowSize;\r\n const baseY1 = tipY - arrowSize / 2;\r\n const baseY2 = tipY + arrowSize / 2;\r\n \r\n const result = `${tipX},${tipY} ${baseX},${baseY1} ${baseX},${baseY2}`;\r\n return result;\r\n } catch (e) {\r\n console.error('箭头生成失败:', e);\r\n return '';\r\n }\r\n };\r\n \r\n // 生成依赖连线的箭头点\r\n const generateDependencyArrowPoints = (\r\n sourcePos: any, \r\n targetPos: any, \r\n linkType: LinkType, \r\n arrowSize: number\r\n ): string => {\r\n if (!sourcePos || !targetPos) return '';\r\n \r\n try {\r\n let endX: number, endY: number;\r\n let direction: 'left' | 'right' | 'up' | 'down' = 'right';\r\n \r\n switch (linkType) {\r\n case LinkType.FINISH_TO_START:\r\n // 箭头指向目标任务的左边缘\r\n endX = targetPos.leftX;\r\n endY = targetPos.centerY;\r\n direction = 'right';\r\n break;\r\n \r\n case LinkType.START_TO_START:\r\n // 箭头指向目标任务的左边缘\r\n endX = targetPos.leftX;\r\n endY = targetPos.centerY;\r\n direction = 'right';\r\n break;\r\n \r\n case LinkType.FINISH_TO_FINISH:\r\n // 箭头指向目标任务的右边缘\r\n endX = targetPos.rightX;\r\n endY = targetPos.centerY;\r\n direction = 'left';\r\n break;\r\n \r\n case LinkType.START_TO_FINISH:\r\n // 箭头指向目标任务的右边缘\r\n endX = targetPos.rightX;\r\n endY = targetPos.centerY;\r\n direction = 'left';\r\n break;\r\n \r\n default:\r\n endX = targetPos.leftX;\r\n endY = targetPos.centerY;\r\n direction = 'right';\r\n }\r\n \r\n // 根据方向生成箭头\r\n let arrowPoint1X: number, arrowPoint1Y: number;\r\n let arrowPoint2X: number, arrowPoint2Y: number;\r\n \r\n if (direction === 'right') {\r\n // 箭头指向右边\r\n arrowPoint1X = endX - arrowSize;\r\n arrowPoint1Y = endY - arrowSize / 2;\r\n arrowPoint2X = endX - arrowSize;\r\n arrowPoint2Y = endY + arrowSize / 2;\r\n } else {\r\n // 箭头指向左边\r\n arrowPoint1X = endX + arrowSize;\r\n arrowPoint1Y = endY - arrowSize / 2;\r\n arrowPoint2X = endX + arrowSize;\r\n arrowPoint2Y = endY + arrowSize / 2;\r\n }\r\n \r\n return `${endX},${endY} ${arrowPoint1X},${arrowPoint1Y} ${arrowPoint2X},${arrowPoint2Y}`;\r\n } catch (e) {\r\n console.error('依赖箭头生成失败:', e);\r\n return '';\r\n }\r\n };\r\n \r\n // 更新连线\r\n // 获取所有被折叠的子任务ID集合(递归)\r\n const getAllCollapsedChildren = (parentId: any): Set<any> => {\r\n const collapsedChildren = new Set<any>();\r\n \r\n const collectChildren = (pid: any) => {\r\n const children = store.tasks.filter(task => task[store.mapFields.parentId] === pid);\r\n children.forEach(child => {\r\n const childId = child[store.mapFields.id];\r\n collapsedChildren.add(childId);\r\n // 递归收集所有子孙任务\r\n collectChildren(childId);\r\n });\r\n };\r\n \r\n collectChildren(parentId);\r\n return collapsedChildren;\r\n };\r\n \r\n // 检查任务是否被折叠(任务本身或其任何祖先被折叠)\r\n const isTaskCollapsed = (taskId: any): boolean => {\r\n // 检查所有已折叠的任务\r\n for (const collapsedId of store.collapsedTasks) {\r\n const collapsedChildren = getAllCollapsedChildren(collapsedId);\r\n if (collapsedChildren.has(taskId)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n };\r\n \r\n const updateLinks = () => {\r\n console.log('updateLinks called');\r\n const newLinks: TaskLink[] = [];\r\n \r\n // 获取连线类型显示配置\r\n const visibility = props.linkConfig.linkTypeVisibility || {\r\n finishToStart: true,\r\n startToStart: true,\r\n finishToFinish: true,\r\n startToFinish: true,\r\n parentChild: true\r\n };\r\n \r\n // 生成父子关系连线(根据 visibility 控制)\r\n if (visibility.parentChild) {\r\n store.tasks.forEach(task => {\r\n const parentId = task[store.mapFields.parentId];\r\n const childId = task[store.mapFields.id];\r\n \r\n if (parentId && parentId !== '0') {\r\n // 检查子任务是否被折叠,如果被折叠则不显示连线\r\n if (isTaskCollapsed(childId)) {\r\n return; // 跳过该连线\r\n }\r\n \r\n const parentPos = getTaskPosition(parentId);\r\n const childPos = getTaskPosition(childId);\r\n \r\n if (parentPos && childPos) {\r\n const linkId = `parent-child-${parentId}-${childId}`;\r\n \r\n // 为位置信息添加任务ID\r\n const parentPosWithId = { ...parentPos, id: parentId, taskId: parentId };\r\n const childPosWithId = { ...childPos, id: childId, taskId: childId };\r\n \r\n const path = generateLinkPath(parentPosWithId, childPosWithId, LinkType.PARENT_CHILD, linkId);\r\n const arrowPoints = props.linkConfig.showArrow ? \r\n generateArrowPoints(childPos, props.linkConfig.arrowSize, LinkType.PARENT_CHILD) : '';\r\n \r\n newLinks.push({\r\n id: linkId,\r\n sourceId: parentId,\r\n targetId: childId,\r\n type: LinkType.PARENT_CHILD,\r\n path,\r\n arrowPoints,\r\n labelX: childPos.centerX,\r\n labelY: childPos.centerY - 10\r\n });\r\n }\r\n }\r\n });\r\n }\r\n \r\n // 生成任务依赖连线\r\n const dependencies = linkDataManager.getDependencies();\r\n console.log('Dependencies:', dependencies);\r\n \r\n // 根据连线类型检查是否显示\r\n const shouldShowLinkType = (linkType: LinkType): boolean => {\r\n switch (linkType) {\r\n case LinkType.FINISH_TO_START:\r\n return visibility.finishToStart;\r\n case LinkType.START_TO_START:\r\n return visibility.startToStart;\r\n case LinkType.FINISH_TO_FINISH:\r\n return visibility.finishToFinish;\r\n case LinkType.START_TO_FINISH:\r\n return visibility.startToFinish;\r\n case LinkType.PARENT_CHILD:\r\n return visibility.parentChild;\r\n default:\r\n return true;\r\n }\r\n };\r\n \r\n dependencies.forEach(dep => {\r\n // 根据 visibility 过滤连线类型\r\n if (!shouldShowLinkType(dep.type)) {\r\n return;\r\n }\r\n \r\n const sourcePos = getTaskPosition(dep.sourceTaskId);\r\n const targetPos = getTaskPosition(dep.targetTaskId);\r\n \r\n if (sourcePos && targetPos) {\r\n const linkId = `dependency-${dep.id}`;\r\n \r\n // 为位置信息添加任务ID\r\n const sourcePosWithId = { ...sourcePos, id: dep.sourceTaskId, taskId: dep.sourceTaskId };\r\n const targetPosWithId = { ...targetPos, id: dep.targetTaskId, taskId: dep.targetTaskId };\r\n \r\n const path = generateLinkPath(sourcePosWithId, targetPosWithId, dep.type, linkId);\r\n \r\n // 根据连线类型生成箭头\r\n const arrowPoints = props.linkConfig.showArrow ? \r\n generateDependencyArrowPoints(sourcePosWithId, targetPosWithId, dep.type, props.linkConfig.arrowSize) : '';\r\n \r\n // 获取连线类型的标签\r\n const labelMap: Record<LinkType, string> = {\r\n [LinkType.FINISH_TO_START]: 'FS',\r\n [LinkType.START_TO_START]: 'SS',\r\n [LinkType.FINISH_TO_FINISH]: 'FF',\r\n [LinkType.START_TO_FINISH]: 'SF',\r\n [LinkType.PARENT_CHILD]: ''\r\n };\r\n \r\n newLinks.push({\r\n id: linkId,\r\n sourceId: dep.sourceTaskId,\r\n targetId: dep.targetTaskId,\r\n type: dep.type,\r\n path,\r\n arrowPoints,\r\n label: labelMap[dep.type] || '',\r\n labelX: (sourcePos.centerX + targetPos.centerX) / 2,\r\n labelY: (sourcePos.centerY + targetPos.centerY) / 2 - 10\r\n });\r\n }\r\n });\r\n \r\n links.value = newLinks;\r\n };\r\n \r\n // 监听任务变化\r\n watch(() => store.tasks, () => {\r\n // 延迟更新,确保DOM已更新\r\n setTimeout(updateLinks, 50);\r\n }, { deep: true });\r\n \r\n watch(() => store.scale, () => {\r\n setTimeout(updateLinks, 50);\r\n });\r\n \r\n // 监听模式变化(月、日、时切换)\r\n watch(() => store.mode, () => {\r\n // 模式切换时需要重新计算bar位置,延迟更新确保bar重绘完成\r\n setTimeout(updateLinks, 200);\r\n });\r\n \r\n // 监听时间轴变化\r\n watch(() => store.timelineCellCount, () => {\r\n setTimeout(updateLinks, 100);\r\n });\r\n \r\n // 监听甘特图日期范围变化\r\n watch(() => [store.startGanttDate, store.endGanttDate], () => {\r\n setTimeout(updateLinks, 100);\r\n });\r\n \r\n // 监听barDate变化(拖拽和调整大小时会触发)\r\n watch(() => store.barDate, () => {\r\n requestAnimationFrame(updateLinks);\r\n }, { deep: true });\r\n \r\n // 监听连线类型显示配置变化\r\n watch(() => props.linkConfig.linkTypeVisibility, () => {\r\n setTimeout(updateLinks, 50);\r\n }, { deep: true });\r\n \r\n // 监听折叠状态变化\r\n watch(() => store.collapsedTasks, () => {\r\n setTimeout(updateLinks, 50);\r\n }, { deep: true });\r\n \r\n // 监听DOM变化(拖拽时重绘)\r\n let resizeObserver: ResizeObserver | null = null;\r\n let mutationObserver: MutationObserver | null = null;\r\n \r\n onMounted(() => {\r\n // 初始更新\r\n setTimeout(updateLinks, 100);\r\n \r\n // 监听容器大小变化\r\n const container = document.querySelector('.gantt');\r\n if (container) {\r\n resizeObserver = new ResizeObserver(updateLinks);\r\n resizeObserver.observe(container);\r\n \r\n\r\n \r\n // 监听DOM变化(任务条位置变化)\r\n mutationObserver = new MutationObserver((mutations) => {\r\n let shouldUpdate = false;\r\n mutations.forEach(mutation => {\r\n const target = mutation.target as HTMLElement;\r\n // 检查是否是SVG bar元素或其父元素\r\n if (mutation.type === 'attributes') {\r\n const attrName = mutation.attributeName;\r\n if (attrName === 'data-x' || \r\n attrName === 'width' || \r\n attrName === 'style' ||\r\n attrName === 'transform') {\r\n // 检查是否是bar元素\r\n if (target.classList?.contains('bar') || \r\n target.classList?.contains('barRow') ||\r\n target.tagName === 'svg') {\r\n shouldUpdate = true;\r\n }\r\n }\r\n }\r\n });\r\n if (shouldUpdate) {\r\n requestAnimationFrame(updateLinks);\r\n }\r\n });\r\n \r\n mutationObserver.observe(container, {\r\n attributes: true,\r\n subtree: true,\r\n attributeFilter: ['style', 'data-x', 'transform', 'width']\r\n });\r\n }\r\n });\r\n \r\n onUnmounted(() => {\r\n if (resizeObserver) {\r\n resizeObserver.disconnect();\r\n }\r\n if (mutationObserver) {\r\n mutationObserver.disconnect();\r\n }\r\n });\r\n \r\n // 虚线动画相关方法\r\n \r\n // 判断是否为虚线\r\n const isDashedLine = (linkType: LinkType): boolean => {\r\n const style = getLinkStyle(linkType);\r\n return !!(style.dashArray && style.dashArray.length > 0);\r\n };\r\n \r\n // 获取虚线流动动画样式\r\n const getDashAnimationStyle = (link: TaskLink) => {\r\n if (!isDashedLine(link.type) || !props.linkConfig.enableDashAnimation) {\r\n return {};\r\n }\r\n \r\n const style = getLinkStyle(link.type);\r\n const speed = props.linkConfig.dashAnimationSpeed || 0.8; // 更快的默认速度\r\n \r\n // 父子关系连线稍快一些\r\n const adjustedSpeed = link.type === LinkType.PARENT_CHILD ? speed * 0.6 : speed;\r\n \r\n // 计算虚线总长度用于动画\r\n const dashArray = style.dashArray || '5,5';\r\n const dashParts = dashArray.split(',').map(Number);\r\n const dashLength = dashParts.reduce((sum, part) => sum + part, 0);\r\n \r\n return {\r\n '--animation-duration': `${adjustedSpeed}s`,\r\n '--dash-length': `${dashLength}px`\r\n };\r\n };\r\n \r\n return {\r\n links,\r\n updateLinks,\r\n getLinkStyle,\r\n isDashedLine,\r\n getDashAnimationStyle\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.task-links-layer {\r\n pointer-events: none;\r\n \r\n .task-link {\r\n transition: stroke-width 0.2s ease;\r\n \r\n &:hover {\r\n stroke-width: 3;\r\n }\r\n \r\n &.parent-child {\r\n opacity: 0.7;\r\n }\r\n \r\n &.finish-to-start {\r\n opacity: 0.9;\r\n }\r\n \r\n // 虚线流动动画\r\n &.dash-animated {\r\n animation: dashFlow var(--animation-duration, 3s) linear infinite;\r\n }\r\n }\r\n \r\n // 虚线流动动画关键帧\r\n @keyframes dashFlow {\r\n 0% {\r\n stroke-dashoffset: 0;\r\n }\r\n 100% {\r\n stroke-dashoffset: calc(-1 * var(--dash-length, 20px));\r\n }\r\n }\r\n \r\n .task-link-arrow {\r\n transition: fill 0.2s ease;\r\n opacity: 1;\r\n pointer-events: none;\r\n }\r\n \r\n .task-link-label {\r\n font-family: Arial, sans-serif;\r\n user-select: none;\r\n }\r\n}\r\n</style>","<template>\r\n <div ref=\"barContent\" @scroll=\"scroll()\" @mouseover=\"mouseover()\"\r\n v-if=\"tasks\" class=\"content\">\r\n <div class=\"content-inner\" :style=\"{ minHeight: containerHeight + 'px', position: 'relative' }\">\r\n <BarRecursionRow :key=\"`${mode}-${scale}-${timelineCellCount}`\" :rowHeight=\"rowHeight\" :tasks=\"tasks\"></BarRecursionRow>\r\n <!-- 任务连线层 -->\r\n <TaskLinks \r\n :containerWidth=\"containerWidth\" \r\n :containerHeight=\"containerHeight\"\r\n :linkConfig=\"linkConfig\"\r\n />\r\n </div>\r\n </div>\r\n </template>\r\n <script lang=\"ts\">\r\n import { defineComponent, ref, watch, computed, onMounted } from 'vue';\r\n import { store } from '../gantt/Store';\r\n import { useScrollState } from './ShareState';\r\n import { useLinkConfig } from './LinkConfig';\r\n import BarRecursionRow from '../gantt/BarRecursionRow.vue';\r\n import TaskLinks from './TaskLinks.vue';\r\n\r\n export default defineComponent({\r\n props: {\r\n rowHeight: {\r\n type: Number,\r\n required: true\r\n },\r\n headers: {\r\n type: Array,\r\n default: () => []\r\n },\r\n },\r\n components: {\r\n BarRecursionRow,\r\n TaskLinks\r\n },\r\n setup(props) {\r\n const barContent = ref<HTMLDivElement | null>(null);\r\n const { scrollTop, scrollFlag, setScrollTop, setScrollFlag } = useScrollState();\r\n const { config: linkConfig } = useLinkConfig();\r\n\r\n const tasks = computed(() => store.tasks);\r\n const timelineCellCount = computed(() => store.timelineCellCount);\r\n const scale = computed(() => store.scale);\r\n const mode = computed(() => store.mode);\r\n const startGanttDate = computed(() => store.startGanttDate);\r\n const endGanttDate = computed(() => store.endGanttDate);\r\n const mapFields = computed(() => store.mapFields);\r\n \r\n // 计算容器尺寸\r\n const containerWidth = computed(() => {\r\n return timelineCellCount.value * scale.value;\r\n });\r\n \r\n const containerHeight = computed(() => {\r\n return tasks.value.length * props.rowHeight;\r\n });\r\n\r\n // 监听共享的滚动位置变化\r\n watch(scrollTop, (newValue) => {\r\n if (scrollFlag.value && barContent.value) {\r\n barContent.value.scrollTop = newValue;\r\n }\r\n });\r\n\r\n onMounted(() => {\r\n if (barContent.value) {\r\n // 初始化时同步滚动位置\r\n barContent.value.scrollTop = scrollTop.value;\r\n }\r\n });\r\n\r\n const getRootNode = () => {\r\n return tasks.value.filter(obj => obj[mapFields.value['parentId']] === '0');\r\n };\r\n\r\n // 优化:使用requestAnimationFrame优化滚动性能\r\n let rafId: number | null = null;\r\n const scroll = () => {\r\n if (rafId) {\r\n cancelAnimationFrame(rafId);\r\n }\r\n rafId = requestAnimationFrame(() => {\r\n if (barContent.value) {\r\n setScrollFlag(false); // 标记当前面板为主动滚动\r\n setScrollTop(barContent.value.scrollTop);\r\n }\r\n rafId = null;\r\n });\r\n };\r\n\r\n const mouseover = () => {\r\n // 鼠标悬停时不改变滚动标志,让滚动事件处理\r\n };\r\n\r\n return {\r\n barContent,\r\n scrollFlag,\r\n tasks,\r\n timelineCellCount,\r\n scale,\r\n mode,\r\n startGanttDate,\r\n endGanttDate,\r\n mapFields,\r\n getRootNode,\r\n scroll,\r\n mouseover,\r\n setScrollFlag,\r\n containerWidth,\r\n containerHeight,\r\n linkConfig\r\n };\r\n }\r\n });\r\n </script>\r\n <style lang=\"scss\" scoped>\r\n .content {\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: flex-start;\r\n margin: 0px 0px -1px 0px;\r\n font-size: 20px;\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n \r\n .content-inner {\r\n width: 100%;\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: flex-start;\r\n }\r\n }\r\n </style>","<template>\r\n <div ref=\"barContent\" @scroll=\"scroll()\" @mouseover=\"mouseover()\"\r\n v-if=\"tasks\" class=\"content\">\r\n <div class=\"content-inner\" :style=\"{ minHeight: containerHeight + 'px', position: 'relative' }\">\r\n <BarRecursionRow :key=\"`${mode}-${scale}-${timelineCellCount}`\" :rowHeight=\"rowHeight\" :tasks=\"tasks\"></BarRecursionRow>\r\n <!-- 任务连线层 -->\r\n <TaskLinks \r\n :containerWidth=\"containerWidth\" \r\n :containerHeight=\"containerHeight\"\r\n :linkConfig=\"linkConfig\"\r\n />\r\n </div>\r\n </div>\r\n </template>\r\n <script lang=\"ts\">\r\n import { defineComponent, ref, watch, computed, onMounted } from 'vue';\r\n import { store } from '../gantt/Store';\r\n import { useScrollState } from './ShareState';\r\n import { useLinkConfig } from './LinkConfig';\r\n import BarRecursionRow from '../gantt/BarRecursionRow.vue';\r\n import TaskLinks from './TaskLinks.vue';\r\n\r\n export default defineComponent({\r\n props: {\r\n rowHeight: {\r\n type: Number,\r\n required: true\r\n },\r\n headers: {\r\n type: Array,\r\n default: () => []\r\n },\r\n },\r\n components: {\r\n BarRecursionRow,\r\n TaskLinks\r\n },\r\n setup(props) {\r\n const barContent = ref<HTMLDivElement | null>(null);\r\n const { scrollTop, scrollFlag, setScrollTop, setScrollFlag } = useScrollState();\r\n const { config: linkConfig } = useLinkConfig();\r\n\r\n const tasks = computed(() => store.tasks);\r\n const timelineCellCount = computed(() => store.timelineCellCount);\r\n const scale = computed(() => store.scale);\r\n const mode = computed(() => store.mode);\r\n const startGanttDate = computed(() => store.startGanttDate);\r\n const endGanttDate = computed(() => store.endGanttDate);\r\n const mapFields = computed(() => store.mapFields);\r\n \r\n // 计算容器尺寸\r\n const containerWidth = computed(() => {\r\n return timelineCellCount.value * scale.value;\r\n });\r\n \r\n const containerHeight = computed(() => {\r\n return tasks.value.length * props.rowHeight;\r\n });\r\n\r\n // 监听共享的滚动位置变化\r\n watch(scrollTop, (newValue) => {\r\n if (scrollFlag.value && barContent.value) {\r\n barContent.value.scrollTop = newValue;\r\n }\r\n });\r\n\r\n onMounted(() => {\r\n if (barContent.value) {\r\n // 初始化时同步滚动位置\r\n barContent.value.scrollTop = scrollTop.value;\r\n }\r\n });\r\n\r\n const getRootNode = () => {\r\n return tasks.value.filter(obj => obj[mapFields.value['parentId']] === '0');\r\n };\r\n\r\n // 优化:使用requestAnimationFrame优化滚动性能\r\n let rafId: number | null = null;\r\n const scroll = () => {\r\n if (rafId) {\r\n cancelAnimationFrame(rafId);\r\n }\r\n rafId = requestAnimationFrame(() => {\r\n if (barContent.value) {\r\n setScrollFlag(false); // 标记当前面板为主动滚动\r\n setScrollTop(barContent.value.scrollTop);\r\n }\r\n rafId = null;\r\n });\r\n };\r\n\r\n const mouseover = () => {\r\n // 鼠标悬停时不改变滚动标志,让滚动事件处理\r\n };\r\n\r\n return {\r\n barContent,\r\n scrollFlag,\r\n tasks,\r\n timelineCellCount,\r\n scale,\r\n mode,\r\n startGanttDate,\r\n endGanttDate,\r\n mapFields,\r\n getRootNode,\r\n scroll,\r\n mouseover,\r\n setScrollFlag,\r\n containerWidth,\r\n containerHeight,\r\n linkConfig\r\n };\r\n }\r\n });\r\n </script>\r\n <style lang=\"scss\" scoped>\r\n .content {\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: flex-start;\r\n margin: 0px 0px -1px 0px;\r\n font-size: 20px;\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n \r\n .content-inner {\r\n width: 100%;\r\n display: flex;\r\n flex-flow: column nowrap;\r\n align-items: center;\r\n justify-content: flex-start;\r\n }\r\n }\r\n </style>","<template>\r\n <div ref=\"tableBar\" class=\"table\">\r\n <div class=\"header\" :style=\"{ height: `${headersHeight}px` }\">\r\n <div class=\"header-border-top\"></div>\r\n <TimelineHeader :weekHeaders=\"weekHeaders\" :hourHeaders=\"hourHeaders\" :dayHeaders=\"dayHeaders\"\r\n :monthHeaders=\"monthHeaders\"></TimelineHeader>\r\n <div class=\"header-border-bottom\"></div>\r\n </div>\r\n <div class=\"content\" :style=\"{ height: `calc(100% - ${headersHeight}px)`, width: 'fit-content', position: 'relative' }\">\r\n <TableContent :rowHeight='rowHeight'></TableContent>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, onMounted, computed, watch } from 'vue';\r\nimport type { Ref } from 'vue';\r\nimport TimelineHeader from './TimelineHeader.vue';\r\nimport { store } from './Store';\r\nimport dayjs from 'dayjs';\r\nimport sharedState from './ShareState';\r\nimport TableContent from './TableContent.vue';\r\n\r\nexport default defineComponent({\r\n props: {\r\n headersHeight: {\r\n type: Number,\r\n default: 50\r\n },\r\n rowHeight: {\r\n type: Number,\r\n default: 50\r\n }\r\n },\r\n components: {\r\n TimelineHeader,\r\n TableContent\r\n },\r\n setup(props) {\r\n // 引用 tableBar\r\n const tableBar: Ref<HTMLDivElement | null> = ref(null);\r\n \r\n\r\n\r\n // 计算属性\r\n const dayHeaders = computed(() => store.dayHeaders);\r\n const weekHeaders = computed(() => store.weekHeaders);\r\n const monthHeaders = computed(() => store.monthHeaders);\r\n const hourHeaders = computed(() => store.hourHeaders);\r\n const startGanttDate = computed(() => store.startGanttDate);\r\n const mode = computed(() => store.mode);\r\n const scale = computed(() => store.scale);\r\n \r\n // 容器尺寸\r\n const containerWidth = computed(() => {\r\n return store.timelineCellCount * scale.value + 100; // 添加额外空间\r\n });\r\n \r\n const containerHeight = computed(() => {\r\n return store.tasks.length * props.rowHeight + 100; // 使用实际行高\r\n });\r\n\r\n // 滚动到今天的方法\r\n const scrollToToday = () => {\r\n if (tableBar.value) {\r\n switch (mode.value) {\r\n case '月':\r\n case '日':\r\n tableBar.value.scrollLeft = Number(dayjs().diff(dayjs(startGanttDate.value), 'day')) * Number(scale.value);\r\n break;\r\n case '周':\r\n // 周模式:滚动到当前周\r\n const currentWeekStart = dayjs().startOf('isoWeek');\r\n const ganttWeekStart = dayjs(startGanttDate.value).startOf('isoWeek');\r\n tableBar.value.scrollLeft = Number(currentWeekStart.diff(ganttWeekStart, 'week')) * Number(scale.value);\r\n break;\r\n case '时':\r\n tableBar.value.scrollLeft = Number(dayjs().diff(dayjs(startGanttDate.value), 'hour')) * Number(scale.value);\r\n break;\r\n }\r\n }\r\n };\r\n\r\n watch(() => sharedState.shouldScrollToToday, (newValue) => {\r\n if (newValue) {\r\n scrollToToday();\r\n // 重置状态\r\n sharedState.shouldScrollToToday = false;\r\n }\r\n });\r\n\r\n onMounted(() => {\r\n\r\n });\r\n\r\n return {\r\n tableBar,\r\n dayHeaders,\r\n weekHeaders,\r\n monthHeaders,\r\n hourHeaders,\r\n startGanttDate,\r\n mode,\r\n scale,\r\n scrollToToday\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.table {\r\n height: 100%;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n overflow-y: hidden;\r\n\r\n .header {\r\n width: 100%;\r\n border: 0px;\r\n }\r\n\r\n .header-border-top,\r\n .header-border-bottom {\r\n width: 100%;\r\n border-top: 1px solid var(--border);\r\n margin: 0px 0px -1px -1px;\r\n }\r\n\r\n .content {\r\n overflow-y: auto;\r\n overflow-x: auto;\r\n }\r\n}\r\n</style>","<template>\r\n <div ref=\"tableBar\" class=\"table\">\r\n <div class=\"header\" :style=\"{ height: `${headersHeight}px` }\">\r\n <div class=\"header-border-top\"></div>\r\n <TimelineHeader :weekHeaders=\"weekHeaders\" :hourHeaders=\"hourHeaders\" :dayHeaders=\"dayHeaders\"\r\n :monthHeaders=\"monthHeaders\"></TimelineHeader>\r\n <div class=\"header-border-bottom\"></div>\r\n </div>\r\n <div class=\"content\" :style=\"{ height: `calc(100% - ${headersHeight}px)`, width: 'fit-content', position: 'relative' }\">\r\n <TableContent :rowHeight='rowHeight'></TableContent>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, onMounted, computed, watch } from 'vue';\r\nimport type { Ref } from 'vue';\r\nimport TimelineHeader from './TimelineHeader.vue';\r\nimport { store } from './Store';\r\nimport dayjs from 'dayjs';\r\nimport sharedState from './ShareState';\r\nimport TableContent from './TableContent.vue';\r\n\r\nexport default defineComponent({\r\n props: {\r\n headersHeight: {\r\n type: Number,\r\n default: 50\r\n },\r\n rowHeight: {\r\n type: Number,\r\n default: 50\r\n }\r\n },\r\n components: {\r\n TimelineHeader,\r\n TableContent\r\n },\r\n setup(props) {\r\n // 引用 tableBar\r\n const tableBar: Ref<HTMLDivElement | null> = ref(null);\r\n \r\n\r\n\r\n // 计算属性\r\n const dayHeaders = computed(() => store.dayHeaders);\r\n const weekHeaders = computed(() => store.weekHeaders);\r\n const monthHeaders = computed(() => store.monthHeaders);\r\n const hourHeaders = computed(() => store.hourHeaders);\r\n const startGanttDate = computed(() => store.startGanttDate);\r\n const mode = computed(() => store.mode);\r\n const scale = computed(() => store.scale);\r\n \r\n // 容器尺寸\r\n const containerWidth = computed(() => {\r\n return store.timelineCellCount * scale.value + 100; // 添加额外空间\r\n });\r\n \r\n const containerHeight = computed(() => {\r\n return store.tasks.length * props.rowHeight + 100; // 使用实际行高\r\n });\r\n\r\n // 滚动到今天的方法\r\n const scrollToToday = () => {\r\n if (tableBar.value) {\r\n switch (mode.value) {\r\n case '月':\r\n case '日':\r\n tableBar.value.scrollLeft = Number(dayjs().diff(dayjs(startGanttDate.value), 'day')) * Number(scale.value);\r\n break;\r\n case '周':\r\n // 周模式:滚动到当前周\r\n const currentWeekStart = dayjs().startOf('isoWeek');\r\n const ganttWeekStart = dayjs(startGanttDate.value).startOf('isoWeek');\r\n tableBar.value.scrollLeft = Number(currentWeekStart.diff(ganttWeekStart, 'week')) * Number(scale.value);\r\n break;\r\n case '时':\r\n tableBar.value.scrollLeft = Number(dayjs().diff(dayjs(startGanttDate.value), 'hour')) * Number(scale.value);\r\n break;\r\n }\r\n }\r\n };\r\n\r\n watch(() => sharedState.shouldScrollToToday, (newValue) => {\r\n if (newValue) {\r\n scrollToToday();\r\n // 重置状态\r\n sharedState.shouldScrollToToday = false;\r\n }\r\n });\r\n\r\n onMounted(() => {\r\n\r\n });\r\n\r\n return {\r\n tableBar,\r\n dayHeaders,\r\n weekHeaders,\r\n monthHeaders,\r\n hourHeaders,\r\n startGanttDate,\r\n mode,\r\n scale,\r\n scrollToToday\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.table {\r\n height: 100%;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n overflow-y: hidden;\r\n\r\n .header {\r\n width: 100%;\r\n border: 0px;\r\n }\r\n\r\n .header-border-top,\r\n .header-border-bottom {\r\n width: 100%;\r\n border-top: 1px solid var(--border);\r\n margin: 0px 0px -1px -1px;\r\n }\r\n\r\n .content {\r\n overflow-y: auto;\r\n overflow-x: auto;\r\n }\r\n}\r\n</style>","// 甘特图主题系统\r\nexport interface GanttTheme {\r\n id: string;\r\n nameKey: string; // 主题名称的翻译键\r\n descKey: string; // 主题描述的翻译键\r\n preview: string;\r\n cssVariables: Record<string, string>;\r\n}\r\n\r\nexport const ganttThemes: GanttTheme[] = [\r\n {\r\n id: 'metro',\r\n nameKey: 'theme.metro',\r\n descKey: 'theme.metroDesc',\r\n preview: '#0078d4',\r\n cssVariables: {\r\n // Metro 金属主题变量\r\n '--primary': '#0078d4',\r\n '--primary-dark': '#106ebe',\r\n '--primary-light': '#1084d8',\r\n '--secondary': '#0d5aa7',\r\n \r\n '--bg-metal-light': 'linear-gradient(145deg, #ffffff, #f5f5f5)',\r\n '--bg-metal-normal': 'linear-gradient(145deg, #f5f5f5, #e8e8e8)',\r\n '--bg-metal-dark': 'linear-gradient(145deg, #e6e6e6, #d0d0d0)',\r\n '--bg-metal-pressed': 'linear-gradient(145deg, #e0e0e0, #f8f8f8)',\r\n \r\n '--bg-active': 'linear-gradient(145deg, #0078d4, #106ebe)',\r\n '--bg-active-hover': 'linear-gradient(145deg, #1084d8, #0d5aa7)',\r\n '--bg-active-pressed': 'linear-gradient(145deg, #0d5aa7, #1084d8)',\r\n \r\n '--border': '#d0d0d0',\r\n '--border-dark': '#b0b0b0',\r\n '--shadow-inset': 'inset 0 1px 0 rgba(255, 255, 255, 0.8), inset 0 -1px 0 rgba(0, 0, 0, 0.1)',\r\n '--shadow-outset': '0 2px 4px rgba(0, 0, 0, 0.1)',\r\n '--shadow-active': 'inset 0 1px 0 rgba(255, 255, 255, 0.2), inset 0 -1px 0 rgba(0, 0, 0, 0.3)',\r\n \r\n '--text-primary': '#333333',\r\n '--text-secondary': '#666666',\r\n '--text-muted': '#999999',\r\n '--text-white': '#ffffff',\r\n \r\n '--bg-primary': '#f8f8f8',\r\n '--bg-secondary': '#e8e8e8',\r\n '--bg-content': '#ffffff',\r\n '--row-hover': '#FFF3A1',\r\n \r\n '--transition-fast': '0.15s ease',\r\n '--transition-normal': '0.25s ease',\r\n '--transition-slow': '0.35s ease',\r\n \r\n '--font-family': \"'Segoe UI', Tahoma, Geneva, Verdana, sans-serif\",\r\n '--font-size-base': '12px',\r\n '--font-weight-normal': '500',\r\n '--font-weight-bold': '600'\r\n }\r\n },\r\n {\r\n id: 'dark',\r\n nameKey: 'theme.dark',\r\n descKey: 'theme.darkDesc',\r\n preview: '#00d4ff',\r\n cssVariables: {\r\n '--primary': '#00d4ff',\r\n '--primary-dark': '#00b8e6',\r\n '--primary-light': '#33ddff',\r\n '--secondary': '#0099cc',\r\n \r\n '--bg-metal-light': 'linear-gradient(145deg, #3a3a3a, #2d2d2d)',\r\n '--bg-metal-normal': 'linear-gradient(145deg, #2d2d2d, #1f1f1f)',\r\n '--bg-metal-dark': 'linear-gradient(145deg, #1f1f1f, #0d0d0d)',\r\n '--bg-metal-pressed': 'linear-gradient(145deg, #0d0d0d, #2d2d2d)',\r\n \r\n '--bg-active': 'linear-gradient(145deg, #00d4ff, #0099cc)',\r\n '--bg-active-hover': 'linear-gradient(145deg, #33ddff, #00b8e6)',\r\n '--bg-active-pressed': 'linear-gradient(145deg, #00b8e6, #33ddff)',\r\n \r\n '--border': '#404040',\r\n '--border-dark': '#2d2d2d',\r\n '--shadow-inset': 'inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 -1px 0 rgba(0, 0, 0, 0.3)',\r\n '--shadow-outset': '0 2px 8px rgba(0, 0, 0, 0.3)',\r\n '--shadow-active': 'inset 0 1px 0 rgba(255, 255, 255, 0.2), inset 0 -1px 0 rgba(0, 0, 0, 0.5)',\r\n \r\n '--text-primary': '#ffffff',\r\n '--text-secondary': '#cccccc',\r\n '--text-muted': '#888888',\r\n '--text-white': '#ffffff',\r\n \r\n '--bg-primary': '#1a1a1a',\r\n '--bg-secondary': '#2d2d2d',\r\n '--bg-content': '#262626',\r\n '--row-hover': '#3a4a5a',\r\n \r\n '--transition-fast': '0.15s ease',\r\n '--transition-normal': '0.25s ease',\r\n '--transition-slow': '0.35s ease',\r\n \r\n '--font-family': \"'Segoe UI', Tahoma, Geneva, Verdana, sans-serif\",\r\n '--font-size-base': '12px',\r\n '--font-weight-normal': '500',\r\n '--font-weight-bold': '600'\r\n }\r\n },\r\n {\r\n id: 'modern',\r\n nameKey: 'theme.light',\r\n descKey: 'theme.lightDesc',\r\n preview: '#6366f1',\r\n cssVariables: {\r\n '--primary': '#6366f1',\r\n '--primary-dark': '#4f46e5',\r\n '--primary-light': '#8b5cf6',\r\n '--secondary': '#3730a3',\r\n \r\n '--bg-metal-light': 'linear-gradient(145deg, #fafafa, #f0f0f0)',\r\n '--bg-metal-normal': 'linear-gradient(145deg, #f0f0f0, #e0e0e0)',\r\n '--bg-metal-dark': 'linear-gradient(145deg, #e0e0e0, #d0d0d0)',\r\n '--bg-metal-pressed': 'linear-gradient(145deg, #d8d8d8, #f0f0f0)',\r\n \r\n '--bg-active': 'linear-gradient(145deg, #6366f1, #4f46e5)',\r\n '--bg-active-hover': 'linear-gradient(145deg, #8b5cf6, #6366f1)',\r\n '--bg-active-pressed': 'linear-gradient(145deg, #4f46e5, #8b5cf6)',\r\n \r\n '--border': '#e0e0e0',\r\n '--border-dark': '#c0c0c0',\r\n '--shadow-inset': 'inset 0 1px 0 rgba(255, 255, 255, 0.9), inset 0 -1px 0 rgba(0, 0, 0, 0.05)',\r\n '--shadow-outset': '0 1px 3px rgba(0, 0, 0, 0.08)',\r\n '--shadow-active': 'inset 0 1px 0 rgba(255, 255, 255, 0.3), inset 0 -1px 0 rgba(0, 0, 0, 0.2)',\r\n \r\n '--text-primary': '#1f2937',\r\n '--text-secondary': '#6b7280',\r\n '--text-muted': '#9ca3af',\r\n '--text-white': '#ffffff',\r\n \r\n '--bg-primary': '#ffffff',\r\n '--bg-secondary': '#f9fafb',\r\n '--bg-content': '#ffffff',\r\n '--row-hover': '#e0e7ff',\r\n \r\n '--transition-fast': '0.15s ease',\r\n '--transition-normal': '0.25s ease',\r\n '--transition-slow': '0.35s ease',\r\n \r\n '--font-family': \"'Inter', 'Segoe UI', sans-serif\",\r\n '--font-size-base': '12px',\r\n '--font-weight-normal': '400',\r\n '--font-weight-bold': '600'\r\n }\r\n },\r\n {\r\n id: 'classic',\r\n nameKey: 'theme.classic',\r\n descKey: 'theme.classicDesc',\r\n preview: '#2563eb',\r\n cssVariables: {\r\n '--primary': '#2563eb',\r\n '--primary-dark': '#1d4ed8',\r\n '--primary-light': '#3b82f6',\r\n '--secondary': '#1e40af',\r\n \r\n '--bg-metal-light': 'linear-gradient(145deg, #f8f9fa, #e9ecef)',\r\n '--bg-metal-normal': 'linear-gradient(145deg, #e9ecef, #dee2e6)',\r\n '--bg-metal-dark': 'linear-gradient(145deg, #dee2e6, #ced4da)',\r\n '--bg-metal-pressed': 'linear-gradient(145deg, #ced4da, #e9ecef)',\r\n \r\n '--bg-active': 'linear-gradient(145deg, #2563eb, #1d4ed8)',\r\n '--bg-active-hover': 'linear-gradient(145deg, #3b82f6, #2563eb)',\r\n '--bg-active-pressed': 'linear-gradient(145deg, #1d4ed8, #3b82f6)',\r\n \r\n '--border': '#ced4da',\r\n '--border-dark': '#adb5bd',\r\n '--shadow-inset': 'inset 0 1px 0 rgba(255, 255, 255, 0.7), inset 0 -1px 0 rgba(0, 0, 0, 0.1)',\r\n '--shadow-outset': '0 2px 4px rgba(0, 0, 0, 0.12)',\r\n '--shadow-active': 'inset 0 1px 0 rgba(255, 255, 255, 0.2), inset 0 -1px 0 rgba(0, 0, 0, 0.25)',\r\n \r\n '--text-primary': '#212529',\r\n '--text-secondary': '#495057',\r\n '--text-muted': '#6c757d',\r\n '--text-white': '#ffffff',\r\n \r\n '--bg-primary': '#ffffff',\r\n '--bg-secondary': '#f8f9fa',\r\n '--bg-content': '#ffffff',\r\n '--row-hover': '#dbeafe',\r\n \r\n '--transition-fast': '0.15s ease',\r\n '--transition-normal': '0.25s ease',\r\n '--transition-slow': '0.35s ease',\r\n \r\n '--font-family': \"'Times New Roman', serif\",\r\n '--font-size-base': '12px',\r\n '--font-weight-normal': '400',\r\n '--font-weight-bold': '700'\r\n }\r\n },\r\n {\r\n id: 'colorful',\r\n nameKey: 'theme.colorful',\r\n descKey: 'theme.colorfulDesc',\r\n preview: '#f59e0b',\r\n cssVariables: {\r\n '--primary': '#f59e0b',\r\n '--primary-dark': '#d97706',\r\n '--primary-light': '#fbbf24',\r\n '--secondary': '#b45309',\r\n \r\n '--bg-metal-light': 'linear-gradient(145deg, #fef3c7, #fed7aa)',\r\n '--bg-metal-normal': 'linear-gradient(145deg, #fed7aa, #fdba74)',\r\n '--bg-metal-dark': 'linear-gradient(145deg, #fdba74, #fb923c)',\r\n '--bg-metal-pressed': 'linear-gradient(145deg, #fb923c, #fed7aa)',\r\n \r\n '--bg-active': 'linear-gradient(145deg, #f59e0b, #d97706)',\r\n '--bg-active-hover': 'linear-gradient(145deg, #fbbf24, #f59e0b)',\r\n '--bg-active-pressed': 'linear-gradient(145deg, #d97706, #fbbf24)',\r\n \r\n '--border': '#fdba74',\r\n '--border-dark': '#fb923c',\r\n '--shadow-inset': 'inset 0 1px 0 rgba(255, 255, 255, 0.6), inset 0 -1px 0 rgba(0, 0, 0, 0.1)',\r\n '--shadow-outset': '0 2px 6px rgba(245, 158, 11, 0.2)',\r\n '--shadow-active': 'inset 0 1px 0 rgba(255, 255, 255, 0.3), inset 0 -1px 0 rgba(0, 0, 0, 0.2)',\r\n \r\n '--text-primary': '#92400e',\r\n '--text-secondary': '#b45309',\r\n '--text-muted': '#d97706',\r\n '--text-white': '#ffffff',\r\n \r\n '--bg-primary': '#fffbeb',\r\n '--bg-secondary': '#fef3c7',\r\n '--bg-content': '#ffffff',\r\n '--row-hover': '#fef3c7',\r\n \r\n '--transition-fast': '0.15s ease',\r\n '--transition-normal': '0.25s ease',\r\n '--transition-slow': '0.35s ease',\r\n \r\n '--font-family': \"'Comic Sans MS', cursive\",\r\n '--font-size-base': '12px',\r\n '--font-weight-normal': '400',\r\n '--font-weight-bold': '600'\r\n }\r\n },\r\n {\r\n id: 'apple',\r\n nameKey: 'theme.apple',\r\n descKey: 'theme.appleDesc',\r\n preview: '#007aff',\r\n cssVariables: {\r\n // Apple/macOS 风格主题变量\r\n '--primary': '#007aff',\r\n '--primary-dark': '#0051d5',\r\n '--primary-light': '#4da3ff',\r\n '--secondary': '#5856d6',\r\n \r\n '--bg-metal-light': 'linear-gradient(145deg, #fafafa, #f2f2f7)',\r\n '--bg-metal-normal': 'linear-gradient(145deg, #f2f2f7, #e5e5ea)',\r\n '--bg-metal-dark': 'linear-gradient(145deg, #e5e5ea, #d1d1d6)',\r\n '--bg-metal-pressed': 'linear-gradient(145deg, #d1d1d6, #f2f2f7)',\r\n \r\n '--bg-active': 'linear-gradient(145deg, #007aff, #0051d5)',\r\n '--bg-active-hover': 'linear-gradient(145deg, #4da3ff, #007aff)',\r\n '--bg-active-pressed': 'linear-gradient(145deg, #0051d5, #4da3ff)',\r\n \r\n '--border': '#d1d1d6',\r\n '--border-dark': '#c7c7cc',\r\n '--shadow-inset': 'inset 0 0.5px 0 rgba(255, 255, 255, 0.9), inset 0 -0.5px 0 rgba(0, 0, 0, 0.08)',\r\n '--shadow-outset': '0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.08)',\r\n '--shadow-active': 'inset 0 1px 0 rgba(255, 255, 255, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.2)',\r\n \r\n '--text-primary': '#000000',\r\n '--text-secondary': '#3c3c43',\r\n '--text-muted': '#8e8e93',\r\n '--text-white': '#ffffff',\r\n \r\n '--bg-primary': '#ffffff',\r\n '--bg-secondary': '#f2f2f7',\r\n '--bg-content': '#ffffff',\r\n '--row-hover': '#e8f4fd',\r\n \r\n '--transition-fast': '0.2s cubic-bezier(0.4, 0, 0.2, 1)',\r\n '--transition-normal': '0.3s cubic-bezier(0.4, 0, 0.2, 1)',\r\n '--transition-slow': '0.4s cubic-bezier(0.4, 0, 0.2, 1)',\r\n \r\n '--font-family': \"'-apple-system', 'BlinkMacSystemFont', 'SF Pro Text', 'Helvetica Neue', sans-serif\",\r\n '--font-size-base': '13px',\r\n '--font-weight-normal': '400',\r\n '--font-weight-bold': '600'\r\n }\r\n }\r\n];\r\n\r\nexport class GanttThemeManager {\r\n private static instance: GanttThemeManager;\r\n private currentTheme: string = 'metro';\r\n private storageKey = 'gantt-theme';\r\n private ganttContainer: HTMLElement | null = null;\r\n\r\n private constructor() {}\r\n\r\n static getInstance(): GanttThemeManager {\r\n if (!GanttThemeManager.instance) {\r\n GanttThemeManager.instance = new GanttThemeManager();\r\n }\r\n return GanttThemeManager.instance;\r\n }\r\n\r\n // 设置甘特图容器\r\n setGanttContainer(container: HTMLElement): void {\r\n console.log('Setting gantt container:', container);\r\n this.ganttContainer = container;\r\n this.loadTheme();\r\n }\r\n\r\n // 获取当前主题\r\n getCurrentTheme(): string {\r\n return this.currentTheme;\r\n }\r\n\r\n // 设置主题\r\n setTheme(themeId: string): void {\r\n const theme = ganttThemes.find(t => t.id === themeId);\r\n if (!theme) {\r\n console.warn(`Theme ${themeId} not found`);\r\n return;\r\n }\r\n\r\n this.currentTheme = themeId;\r\n this.applyTheme(theme);\r\n this.saveTheme();\r\n }\r\n\r\n // 应用主题到甘特图容器\r\n private applyTheme(theme: GanttTheme): void {\r\n if (!this.ganttContainer) {\r\n console.warn('No gantt container available for theme application');\r\n return;\r\n }\r\n\r\n console.log('Applying theme:', theme.id, 'to container:', this.ganttContainer);\r\n\r\n // 设置data-theme属性\r\n this.ganttContainer.setAttribute('data-gantt-theme', theme.id);\r\n\r\n // 应用CSS变量到甘特图容器\r\n Object.entries(theme.cssVariables).forEach(([property, value]) => {\r\n this.ganttContainer!.style.setProperty(property, value);\r\n });\r\n \r\n console.log('Theme applied successfully. Container styles:', this.ganttContainer.style.cssText);\r\n }\r\n\r\n // 从本地存储加载主题\r\n private loadTheme(): void {\r\n try {\r\n const savedTheme = localStorage.getItem(this.storageKey);\r\n if (savedTheme && ganttThemes.find(t => t.id === savedTheme)) {\r\n this.currentTheme = savedTheme;\r\n }\r\n \r\n const theme = ganttThemes.find(t => t.id === this.currentTheme);\r\n if (theme) {\r\n this.applyTheme(theme);\r\n }\r\n } catch (error) {\r\n console.warn('Failed to load theme from localStorage:', error);\r\n }\r\n }\r\n\r\n // 保存主题到本地存储\r\n private saveTheme(): void {\r\n try {\r\n localStorage.setItem(this.storageKey, this.currentTheme);\r\n } catch (error) {\r\n console.warn('Failed to save theme to localStorage:', error);\r\n }\r\n }\r\n\r\n // 获取所有主题\r\n getThemes(): GanttTheme[] {\r\n return ganttThemes;\r\n }\r\n\r\n // 获取主题信息\r\n getThemeInfo(themeId: string): GanttTheme | undefined {\r\n return ganttThemes.find(t => t.id === themeId);\r\n }\r\n\r\n // 预览主题(临时应用,不保存)\r\n previewTheme(themeId: string): void {\r\n const theme = ganttThemes.find(t => t.id === themeId);\r\n if (theme) {\r\n this.applyTheme(theme);\r\n }\r\n }\r\n\r\n // 取消预览,恢复当前主题\r\n cancelPreview(): void {\r\n const theme = ganttThemes.find(t => t.id === this.currentTheme);\r\n if (theme) {\r\n this.applyTheme(theme);\r\n }\r\n }\r\n\r\n // 导出主题配置\r\n exportThemeConfig(): string {\r\n return JSON.stringify({\r\n currentTheme: this.currentTheme,\r\n timestamp: new Date().toISOString()\r\n }, null, 2);\r\n }\r\n\r\n // 导入主题配置\r\n importThemeConfig(configJson: string): boolean {\r\n try {\r\n const config = JSON.parse(configJson);\r\n if (config.currentTheme && ganttThemes.find(t => t.id === config.currentTheme)) {\r\n this.setTheme(config.currentTheme);\r\n return true;\r\n }\r\n return false;\r\n } catch (error) {\r\n console.error('Failed to import theme config:', error);\r\n return false;\r\n }\r\n }\r\n}\r\n\r\n// 导出单例实例\r\nexport const ganttThemeManager = GanttThemeManager.getInstance();","<template>\r\n <div class=\"config-panel-wrapper\">\r\n <button \r\n class=\"config-btn\" \r\n @click=\"togglePanel\"\r\n :class=\"{ active: isOpen }\"\r\n :title=\"t('configPanel.title')\"\r\n >\r\n <div class=\"btn-content\">\r\n <div class=\"btn-icon\">\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z\"/>\r\n </svg>\r\n </div>\r\n <span class=\"btn-text\">{{ t('common.config') }}</span>\r\n </div>\r\n </button>\r\n\r\n <transition name=\"panel-fade\">\r\n <div v-if=\"isOpen\" class=\"config-panel\" @click.stop>\r\n <div class=\"panel-header\">\r\n <h3>{{ t('configPanel.title') }}</h3>\r\n <button class=\"close-btn\" @click=\"closePanel\" :title=\"t('common.close')\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <div class=\"panel-content\">\r\n <!-- 语言配置区域 -->\r\n <div class=\"config-section\">\r\n <h4 class=\"section-title\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z\"/>\r\n </svg>\r\n {{ t('configPanel.languageSettings') }}\r\n </h4>\r\n <div class=\"language-selector-inline\">\r\n <div\r\n v-for=\"locale in locales\"\r\n :key=\"locale.value\"\r\n class=\"language-option\"\r\n :class=\"{ active: currentLocale === locale.value }\"\r\n @click=\"selectLocale(locale.value)\"\r\n >\r\n <span class=\"lang-label\">{{ locale.label }}</span>\r\n <div v-if=\"currentLocale === locale.value\" class=\"lang-check\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/>\r\n </svg>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 主题配置区域 -->\r\n <div class=\"config-section\">\r\n <h4 class=\"section-title\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z\"/>\r\n </svg>\r\n {{ t('configPanel.themeSettings') }}\r\n </h4>\r\n <div class=\"theme-grid\">\r\n <div\r\n v-for=\"theme in themes\"\r\n :key=\"theme.id\"\r\n class=\"theme-card\"\r\n :class=\"{ active: currentTheme === theme.id }\"\r\n @click=\"selectTheme(theme.id)\"\r\n >\r\n <div class=\"theme-preview\" :style=\"{ background: theme.preview }\"></div>\r\n <div class=\"theme-info\">\r\n <div class=\"theme-name\">{{ t(theme.nameKey) }}</div>\r\n <div class=\"theme-desc\">{{ t(theme.descKey) }}</div>\r\n </div>\r\n <div v-if=\"currentTheme === theme.id\" class=\"theme-check\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/>\r\n </svg>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 连线配置区域 -->\r\n <div class=\"config-section\">\r\n <h4 class=\"section-title\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M6 9l6 6 6-6\"/>\r\n <circle cx=\"4\" cy=\"9\" r=\"2\"/>\r\n <circle cx=\"20\" cy=\"9\" r=\"2\"/>\r\n <path d=\"M10 9h4\"/>\r\n </svg>\r\n {{ t('configPanel.linkSettings') }}\r\n </h4>\r\n <div class=\"link-config-content\">\r\n <!-- 说明文字 -->\r\n <div class=\"config-info-box\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" style=\"flex-shrink: 0;\">\r\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/>\r\n </svg>\r\n <span>{{ t('configPanel.linkConfig.info') }}</span>\r\n </div>\r\n\r\n <!-- 路径类型选择 -->\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.pathType') }}</label>\r\n <div class=\"path-type-grid\">\r\n <div \r\n v-for=\"pathType in pathTypes\" \r\n :key=\"pathType.value\"\r\n class=\"path-type-card\"\r\n :class=\"{ active: linkConfig.pathType === pathType.value }\"\r\n @click=\"selectPathType(pathType.value)\"\r\n >\r\n <svg width=\"50\" height=\"30\" viewBox=\"0 0 60 40\">\r\n <path \r\n :d=\"pathType.preview\" \r\n stroke=\"currentColor\" \r\n stroke-width=\"2\" \r\n fill=\"none\"\r\n />\r\n </svg>\r\n <span class=\"path-name\">{{ pathType.name }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 基础样式配置 -->\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.color') }}</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.color\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.width') }}: {{ linkConfig.width }}px</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.width\" \r\n @input=\"updateLinkConfig\"\r\n min=\"1\" \r\n max=\"5\" \r\n step=\"0.5\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.dashStyle') }}</label>\r\n <select v-model=\"linkConfig.dashArray\" @change=\"updateLinkConfig\" class=\"select-input\">\r\n <option :value=\"undefined\">{{ t('configPanel.linkConfig.solid') }}</option>\r\n <option value=\"3,3\">{{ t('configPanel.linkConfig.shortDash') }}</option>\r\n <option value=\"5,5\">{{ t('configPanel.linkConfig.mediumDash') }}</option>\r\n <option value=\"8,4\">{{ t('configPanel.linkConfig.longDash') }}</option>\r\n <option value=\"2,2,8,2\">{{ t('configPanel.linkConfig.dotDash') }}</option>\r\n </select>\r\n </div>\r\n\r\n <!-- 贝塞尔曲线配置 -->\r\n <div v-if=\"linkConfig.pathType === 'bezier'\" class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.curvature') }}: {{ linkConfig.bezierCurvature }}</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.bezierCurvature\" \r\n @input=\"updateLinkConfig\"\r\n min=\"0.1\" \r\n max=\"1\" \r\n step=\"0.1\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n\r\n <!-- 直角连线配置 -->\r\n <template v-if=\"linkConfig.pathType === 'right-angle'\">\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.offset') }}: {{ linkConfig.rightAngleOffset }}px</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.rightAngleOffset\" \r\n @input=\"updateLinkConfig\"\r\n min=\"10\" \r\n max=\"80\" \r\n step=\"5\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"linkConfig.smoothCorners\" \r\n @change=\"updateLinkConfig\"\r\n />\r\n {{ t('configPanel.linkConfig.smoothCorners') }}\r\n </label>\r\n </div>\r\n\r\n <div v-if=\"linkConfig.smoothCorners\" class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.cornerRadius') }}: {{ linkConfig.cornerRadius }}px</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.cornerRadius\" \r\n @input=\"updateLinkConfig\"\r\n min=\"0\" \r\n max=\"15\" \r\n step=\"1\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n </template>\r\n\r\n <!-- 箭头配置 -->\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"linkConfig.showArrow\" \r\n @change=\"updateLinkConfig\"\r\n />\r\n {{ t('configPanel.linkConfig.showArrow') }}\r\n </label>\r\n </div>\r\n\r\n <template v-if=\"linkConfig.showArrow\">\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.arrowSize') }}: {{ linkConfig.arrowSize }}px</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.arrowSize\" \r\n @input=\"updateLinkConfig\"\r\n min=\"4\" \r\n max=\"16\" \r\n step=\"1\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.arrowColor') }}</label>\r\n <div class=\"color-group\">\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.arrowColor\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input\"\r\n />\r\n <button \r\n @click=\"linkConfig.arrowColor = linkConfig.color; updateLinkConfig()\" \r\n class=\"sync-btn\"\r\n :title=\"t('tooltip.syncArrowColor')\"\r\n >\r\n {{ t('configPanel.linkConfig.syncColor') }}\r\n </button>\r\n </div>\r\n </div>\r\n </template>\r\n\r\n <!-- 虚线动画 -->\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"linkConfig.enableDashAnimation\" \r\n @change=\"updateLinkConfig\"\r\n />\r\n {{ t('configPanel.linkConfig.dashAnimation') }}\r\n </label>\r\n </div>\r\n\r\n <div v-if=\"linkConfig.enableDashAnimation\" class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.animationSpeed') }}: {{ linkConfig.dashAnimationSpeed }}s</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.dashAnimationSpeed\" \r\n @input=\"updateLinkConfig\"\r\n min=\"0.2\" \r\n max=\"3\" \r\n step=\"0.2\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n\r\n <!-- 标签配置 -->\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"linkConfig.showLabels\" \r\n @change=\"updateLinkConfig\"\r\n />\r\n {{ t('configPanel.linkConfig.showLabels') }}\r\n </label>\r\n </div>\r\n\r\n <template v-if=\"linkConfig.showLabels\">\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.labelColor') }}</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.labelColor\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.fontSize') }}: {{ linkConfig.labelFontSize }}px</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.labelFontSize\" \r\n @input=\"updateLinkConfig\"\r\n min=\"8\" \r\n max=\"16\" \r\n step=\"1\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n </template>\r\n\r\n <!-- 连线类型颜色配置 -->\r\n <div class=\"config-subsection\">\r\n <h5 class=\"subsection-title\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" style=\"margin-right: 4px;\">\r\n <path d=\"M19 15l-6 6-1.42-1.42L15.17 16H4V4h2v10h9.17l-3.59-3.58L13 9l6 6z\"/>\r\n </svg>\r\n {{ t('configPanel.linkConfig.typeColors') }}\r\n </h5>\r\n \r\n <div class=\"link-type-colors\">\r\n <div class=\"color-item\">\r\n <div class=\"color-preview\">\r\n <svg width=\"20\" height=\"10\" viewBox=\"0 0 20 10\">\r\n <line x1=\"0\" y1=\"5\" x2=\"14\" y2=\"5\" :stroke=\"linkConfig.linkTypeColors.finishToStart\" stroke-width=\"2\"/>\r\n <polygon :points=\"'20,5 14,2 14,8'\" :fill=\"linkConfig.linkTypeColors.finishToStart\"/>\r\n </svg>\r\n </div>\r\n <span class=\"color-label\">{{ t('link.fs') }}</span>\r\n <span class=\"color-desc\">{{ t('link.finishToStart') }}</span>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.linkTypeColors.finishToStart\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input-small\"\r\n />\r\n </div>\r\n \r\n <div class=\"color-item\">\r\n <div class=\"color-preview\">\r\n <svg width=\"20\" height=\"10\" viewBox=\"0 0 20 10\">\r\n <line x1=\"0\" y1=\"5\" x2=\"14\" y2=\"5\" :stroke=\"linkConfig.linkTypeColors.startToStart\" stroke-width=\"2\"/>\r\n <polygon :points=\"'20,5 14,2 14,8'\" :fill=\"linkConfig.linkTypeColors.startToStart\"/>\r\n </svg>\r\n </div>\r\n <span class=\"color-label\">{{ t('link.ss') }}</span>\r\n <span class=\"color-desc\">{{ t('link.startToStart') }}</span>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.linkTypeColors.startToStart\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input-small\"\r\n />\r\n </div>\r\n \r\n <div class=\"color-item\">\r\n <div class=\"color-preview\">\r\n <svg width=\"20\" height=\"10\" viewBox=\"0 0 20 10\">\r\n <line x1=\"0\" y1=\"5\" x2=\"14\" y2=\"5\" :stroke=\"linkConfig.linkTypeColors.finishToFinish\" stroke-width=\"2\"/>\r\n <polygon :points=\"'20,5 14,2 14,8'\" :fill=\"linkConfig.linkTypeColors.finishToFinish\"/>\r\n </svg>\r\n </div>\r\n <span class=\"color-label\">{{ t('link.ff') }}</span>\r\n <span class=\"color-desc\">{{ t('link.finishToFinish') }}</span>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.linkTypeColors.finishToFinish\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input-small\"\r\n />\r\n </div>\r\n \r\n <div class=\"color-item\">\r\n <div class=\"color-preview\">\r\n <svg width=\"20\" height=\"10\" viewBox=\"0 0 20 10\">\r\n <line x1=\"0\" y1=\"5\" x2=\"14\" y2=\"5\" :stroke=\"linkConfig.linkTypeColors.startToFinish\" stroke-width=\"2\"/>\r\n <polygon :points=\"'20,5 14,2 14,8'\" :fill=\"linkConfig.linkTypeColors.startToFinish\"/>\r\n </svg>\r\n </div>\r\n <span class=\"color-label\">{{ t('link.sf') }}</span>\r\n <span class=\"color-desc\">{{ t('link.startToFinish') }}</span>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.linkTypeColors.startToFinish\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input-small\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 父子关系样式 -->\r\n <div class=\"config-subsection\">\r\n <h5 class=\"subsection-title\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" style=\"margin-right: 4px;\">\r\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/>\r\n </svg>\r\n {{ t('configPanel.linkConfig.parentChildStyle') }}\r\n </h5>\r\n <div class=\"config-info-box\" style=\"margin-bottom: 12px;\">\r\n <span style=\"font-size: 11px;\">{{ t('configPanel.linkConfig.parentChildInfo') }}</span>\r\n </div>\r\n \r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.color') }}</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.parentChildStyle.color\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.width') }}: {{ linkConfig.parentChildStyle.width }}px</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.parentChildStyle.width\" \r\n @input=\"updateLinkConfig\"\r\n min=\"1\" \r\n max=\"3\" \r\n step=\"0.5\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.dashStyle') }}</label>\r\n <select v-model=\"linkConfig.parentChildStyle.dashArray\" @change=\"updateLinkConfig\" class=\"select-input\">\r\n <option :value=\"undefined\">{{ t('configPanel.linkConfig.solid') }}</option>\r\n <option value=\"3,3\">{{ t('configPanel.linkConfig.shortDash') }}</option>\r\n <option value=\"5,5\">{{ t('configPanel.linkConfig.mediumDash') }}</option>\r\n <option value=\"8,4\">{{ t('configPanel.linkConfig.longDash') }}</option>\r\n </select>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </transition>\r\n\r\n <div v-if=\"isOpen\" class=\"panel-overlay\" @click=\"closePanel\"></div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, onMounted, watch, inject, computed, type Ref } from 'vue';\r\nimport { ganttThemes, ganttThemeManager, type GanttTheme } from './themes/GanttThemes';\r\nimport { useLinkConfig, LinkPathType } from './LinkConfig';\r\nimport { useI18n, type Locale } from './i18n';\r\n\r\nexport default defineComponent({\r\n name: 'GanttConfigPanel',\r\n setup() {\r\n const { t, locale, setLocale, getLocales } = useI18n();\r\n const isOpen = ref(false);\r\n const themes = ref<GanttTheme[]>(ganttThemes);\r\n const currentTheme = ref<string>('metro');\r\n \r\n // 语言选择器相关\r\n const currentLocale = computed(() => locale.value);\r\n const locales = computed(() => getLocales());\r\n \r\n const selectLocale = (localeValue: Locale) => {\r\n setLocale(localeValue);\r\n };\r\n \r\n // 注入甘特图容器引用\r\n const ganttContainer = inject<Ref<HTMLElement | undefined>>('ganttContainer');\r\n\r\n // 使用连线配置管理器\r\n const { config: linkConfigManager, updateConfig: updateLinkConfigManager } = useLinkConfig();\r\n\r\n // 连线配置 - 从管理器初始化\r\n const linkConfig = ref({\r\n pathType: linkConfigManager.pathType || LinkPathType.BEZIER,\r\n color: linkConfigManager.color || '#3498db',\r\n width: linkConfigManager.width || 2,\r\n dashArray: linkConfigManager.dashArray,\r\n bezierCurvature: linkConfigManager.bezierCurvature || 0.5,\r\n rightAngleOffset: linkConfigManager.rightAngleOffset || 40,\r\n smoothCorners: linkConfigManager.smoothCorners || false,\r\n cornerRadius: linkConfigManager.cornerRadius || 5,\r\n showArrow: linkConfigManager.showArrow !== undefined ? linkConfigManager.showArrow : true,\r\n arrowSize: linkConfigManager.arrowSize || 8,\r\n arrowColor: linkConfigManager.arrowColor || '#3498db',\r\n enableDashAnimation: linkConfigManager.enableDashAnimation || false,\r\n dashAnimationSpeed: linkConfigManager.dashAnimationSpeed || 1,\r\n showLabels: linkConfigManager.showLabels || false,\r\n labelColor: linkConfigManager.labelColor || '#333333',\r\n labelFontSize: linkConfigManager.labelFontSize || 12,\r\n parentChildStyle: {\r\n color: linkConfigManager.parentChildStyle?.color || '#999999',\r\n width: linkConfigManager.parentChildStyle?.width || 1.5,\r\n dashArray: linkConfigManager.parentChildStyle?.dashArray || '3,3'\r\n },\r\n linkTypeColors: {\r\n finishToStart: linkConfigManager.linkTypeColors?.finishToStart || '#3498db',\r\n startToStart: linkConfigManager.linkTypeColors?.startToStart || '#2ecc71',\r\n finishToFinish: linkConfigManager.linkTypeColors?.finishToFinish || '#e74c3c',\r\n startToFinish: linkConfigManager.linkTypeColors?.startToFinish || '#f39c12'\r\n },\r\n linkTypeVisibility: {\r\n finishToStart: linkConfigManager.linkTypeVisibility?.finishToStart ?? true,\r\n startToStart: linkConfigManager.linkTypeVisibility?.startToStart ?? true,\r\n finishToFinish: linkConfigManager.linkTypeVisibility?.finishToFinish ?? true,\r\n startToFinish: linkConfigManager.linkTypeVisibility?.startToFinish ?? true,\r\n parentChild: linkConfigManager.linkTypeVisibility?.parentChild ?? true\r\n }\r\n });\r\n\r\n // 路径类型 - 使用 computed 以响应语言变化\r\n const pathTypes = computed(() => [\r\n { value: LinkPathType.STRAIGHT, name: t('configPanel.linkConfig.straight'), preview: 'M 10 20 L 50 20' },\r\n { value: LinkPathType.BEZIER, name: t('configPanel.linkConfig.bezier'), preview: 'M 10 20 C 25 20 35 20 50 20' },\r\n { value: LinkPathType.RIGHT_ANGLE, name: t('configPanel.linkConfig.rightAngle'), preview: 'M 10 20 L 30 20 L 30 30 L 50 30' }\r\n ]);\r\n\r\n const togglePanel = () => {\r\n isOpen.value = !isOpen.value;\r\n };\r\n\r\n const closePanel = () => {\r\n isOpen.value = false;\r\n };\r\n\r\n const selectTheme = (themeId: string) => {\r\n currentTheme.value = themeId;\r\n ganttThemeManager.setTheme(themeId);\r\n };\r\n\r\n const selectPathType = (pathType: string) => {\r\n linkConfig.value.pathType = pathType as LinkPathType;\r\n updateLinkConfig();\r\n };\r\n\r\n const updateLinkConfig = () => {\r\n // 更新到连线配置管理器\r\n updateLinkConfigManager(linkConfig.value);\r\n console.log('Link config updated:', linkConfig.value);\r\n };\r\n\r\n onMounted(() => {\r\n // 设置甘特图容器到主题管理器\r\n if (ganttContainer?.value) {\r\n console.log('GanttConfigPanel: Setting gantt container to theme manager');\r\n ganttThemeManager.setGanttContainer(ganttContainer.value);\r\n } else {\r\n console.warn('GanttConfigPanel: ganttContainer not available');\r\n }\r\n \r\n currentTheme.value = ganttThemeManager.getCurrentTheme();\r\n });\r\n\r\n // 监听linkConfig的深层变化\r\n watch(linkConfig, (newConfig) => {\r\n updateLinkConfigManager(newConfig);\r\n }, { deep: true });\r\n\r\n return {\r\n t,\r\n isOpen,\r\n themes,\r\n currentTheme,\r\n linkConfig,\r\n pathTypes,\r\n currentLocale,\r\n locales,\r\n togglePanel,\r\n closePanel,\r\n selectTheme,\r\n selectPathType,\r\n selectLocale,\r\n updateLinkConfig\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.config-panel-wrapper {\r\n position: relative;\r\n}\r\n\r\n.config-btn {\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border: 1px solid var(--border, #d0d0d0);\r\n box-shadow: var(--shadow-inset, inset 0 1px 0 rgba(255, 255, 255, 0.8));\r\n padding: 8px 16px;\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n font-family: var(--font-family);\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n\r\n &:hover {\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n }\r\n\r\n &:active, &.active {\r\n background: var(--bg-active, linear-gradient(145deg, #0078d4, #106ebe));\r\n \r\n .btn-icon, .btn-text {\r\n color: var(--text-white, #ffffff);\r\n }\r\n }\r\n\r\n .btn-content {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n }\r\n\r\n .btn-icon {\r\n color: var(--text-secondary, #666666);\r\n display: flex;\r\n align-items: center;\r\n transition: color var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n .btn-text {\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--text-primary, #333333);\r\n transition: color var(--transition-fast, 0.15s ease);\r\n }\r\n}\r\n\r\n.panel-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: rgba(0, 0, 0, 0.3);\r\n z-index: 999;\r\n}\r\n\r\n.config-panel {\r\n position: fixed;\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%, -50%);\r\n width: 90%;\r\n max-width: 1000px;\r\n max-height: 85vh;\r\n background: var(--bg-content, #ffffff);\r\n border: 1px solid var(--border, #d0d0d0);\r\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\r\n z-index: 1000;\r\n display: flex;\r\n flex-direction: column;\r\n font-family: var(--font-family);\r\n}\r\n\r\n.panel-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 20px 24px;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border-bottom: 1px solid var(--border, #d0d0d0);\r\n\r\n h3 {\r\n margin: 0;\r\n font-size: 18px;\r\n font-weight: 600;\r\n color: var(--text-primary, #333333);\r\n }\r\n\r\n .close-btn {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 4px;\r\n color: var(--text-secondary, #666666);\r\n transition: all var(--transition-fast, 0.15s ease);\r\n display: flex;\r\n align-items: center;\r\n\r\n &:hover {\r\n color: var(--primary, #0078d4);\r\n transform: scale(1.1);\r\n }\r\n }\r\n}\r\n\r\n.panel-content {\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 24px;\r\n display: grid;\r\n grid-template-columns: 1fr 1fr;\r\n gap: 24px;\r\n}\r\n\r\n.config-section {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n\r\n .section-title {\r\n margin: 0;\r\n font-size: 14px;\r\n font-weight: 600;\r\n color: var(--text-primary, #333333);\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding-bottom: 12px;\r\n border-bottom: 2px solid var(--border, #d0d0d0);\r\n\r\n svg {\r\n color: var(--primary, #0078d4);\r\n }\r\n }\r\n}\r\n\r\n.theme-grid {\r\n display: grid;\r\n grid-template-columns: 1fr;\r\n gap: 12px;\r\n}\r\n\r\n.theme-card {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n padding: 12px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 2px solid var(--border, #d0d0d0);\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n position: relative;\r\n\r\n &:hover {\r\n border-color: var(--primary, #0078d4);\r\n transform: translateY(-2px);\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\r\n }\r\n\r\n &.active {\r\n border-color: var(--primary, #0078d4);\r\n background: var(--bg-content, #ffffff);\r\n box-shadow: 0 0 0 2px var(--primary, #0078d4), 0 2px 8px rgba(0, 120, 212, 0.15);\r\n \r\n .theme-info {\r\n .theme-name {\r\n color: var(--primary, #0078d4);\r\n }\r\n \r\n .theme-desc {\r\n color: var(--text-primary, #333333);\r\n }\r\n }\r\n }\r\n\r\n .theme-preview {\r\n width: 40px;\r\n height: 40px;\r\n border-radius: 4px;\r\n flex-shrink: 0;\r\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);\r\n }\r\n\r\n .theme-info {\r\n flex: 1;\r\n min-width: 0;\r\n\r\n .theme-name {\r\n font-size: 13px;\r\n font-weight: 600;\r\n color: var(--text-primary, #333333);\r\n margin-bottom: 4px;\r\n }\r\n\r\n .theme-desc {\r\n font-size: 11px;\r\n color: var(--text-secondary, #666666);\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n }\r\n }\r\n\r\n .theme-check {\r\n color: var(--primary, #0078d4);\r\n flex-shrink: 0;\r\n }\r\n}\r\n\r\n.language-selector-inline {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\r\n gap: 8px;\r\n \r\n .language-option {\r\n padding: 10px 12px;\r\n border: 1px solid var(--border, #d0d0d0);\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n box-shadow: var(--shadow-inset, inset 0 1px 0 rgba(255, 255, 255, 0.8));\r\n\r\n &:hover {\r\n border-color: var(--primary, #0078d4);\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n transform: translateY(-1px);\r\n }\r\n\r\n &.active {\r\n border-color: var(--primary, #0078d4);\r\n background: var(--bg-content, #ffffff);\r\n box-shadow: 0 0 0 2px var(--primary, #0078d4), 0 2px 8px rgba(0, 120, 212, 0.15);\r\n \r\n .lang-label {\r\n color: var(--primary, #0078d4);\r\n font-weight: 600;\r\n }\r\n }\r\n\r\n .lang-label {\r\n font-size: 12px;\r\n flex: 1;\r\n color: var(--text-primary, #333333);\r\n }\r\n\r\n .lang-check {\r\n color: var(--primary, #0078d4);\r\n display: flex;\r\n align-items: center;\r\n margin-left: 8px;\r\n }\r\n }\r\n}\r\n\r\n.link-config-content {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n\r\n .config-group {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n\r\n .config-label {\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--text-primary, #333333);\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n }\r\n\r\n .color-input {\r\n width: 60px;\r\n height: 36px;\r\n border: 1px solid var(--border, #d0d0d0);\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n\r\n &:hover {\r\n border-color: var(--primary, #0078d4);\r\n }\r\n }\r\n\r\n .range-input {\r\n width: 100%;\r\n height: 6px;\r\n -webkit-appearance: none;\r\n appearance: none;\r\n background: var(--bg-secondary, #e8e8e8);\r\n outline: none;\r\n border-radius: 3px;\r\n\r\n &::-webkit-slider-thumb {\r\n -webkit-appearance: none;\r\n appearance: none;\r\n width: 16px;\r\n height: 16px;\r\n background: var(--primary, #0078d4);\r\n cursor: pointer;\r\n border-radius: 50%;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n\r\n &:hover {\r\n transform: scale(1.2);\r\n }\r\n }\r\n\r\n &::-moz-range-thumb {\r\n width: 16px;\r\n height: 16px;\r\n background: var(--primary, #0078d4);\r\n cursor: pointer;\r\n border-radius: 50%;\r\n border: none;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n\r\n &:hover {\r\n transform: scale(1.2);\r\n }\r\n }\r\n }\r\n }\r\n\r\n .path-type-grid {\r\n display: grid;\r\n grid-template-columns: repeat(3, 1fr);\r\n gap: 8px;\r\n }\r\n\r\n .path-type-card {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 12px 8px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 2px solid var(--border, #d0d0d0);\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n color: var(--text-secondary, #666666);\r\n\r\n &:hover {\r\n border-color: var(--primary, #0078d4);\r\n color: var(--primary, #0078d4);\r\n }\r\n\r\n &.active {\r\n border-color: var(--primary, #0078d4);\r\n background: var(--bg-content, #ffffff);\r\n box-shadow: 0 0 0 2px var(--primary, #0078d4), 0 2px 8px rgba(0, 120, 212, 0.15);\r\n \r\n .path-name {\r\n color: var(--primary, #0078d4);\r\n }\r\n }\r\n\r\n .path-name {\r\n font-size: 11px;\r\n font-weight: 600;\r\n }\r\n }\r\n\r\n .select-input {\r\n width: 100%;\r\n padding: 8px 12px;\r\n border: 1px solid var(--border, #d0d0d0);\r\n background: var(--bg-content, #ffffff);\r\n color: var(--text-primary, #333333);\r\n font-size: 12px;\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n\r\n &:hover {\r\n border-color: var(--primary, #0078d4);\r\n }\r\n\r\n &:focus {\r\n outline: none;\r\n border-color: var(--primary, #0078d4);\r\n box-shadow: 0 0 0 2px rgba(0, 120, 212, 0.1);\r\n }\r\n }\r\n\r\n .color-group {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n }\r\n\r\n .sync-btn {\r\n padding: 6px 12px;\r\n font-size: 11px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n color: var(--text-primary, #333333);\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n white-space: nowrap;\r\n\r\n &:hover {\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border-color: var(--primary, #0078d4);\r\n }\r\n\r\n &:active {\r\n transform: scale(0.95);\r\n }\r\n }\r\n\r\n .config-subsection {\r\n margin-top: 16px;\r\n padding-top: 16px;\r\n border-top: 1px solid var(--border, #d0d0d0);\r\n\r\n .subsection-title {\r\n margin: 0 0 12px 0;\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--text-secondary, #666666);\r\n display: flex;\r\n align-items: center;\r\n }\r\n }\r\n\r\n .config-info-box {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 8px;\r\n padding: 8px 12px;\r\n background: var(--bg-secondary, #f8f9fa);\r\n border-left: 3px solid var(--primary, #0078d4);\r\n font-size: 11px;\r\n color: var(--text-secondary, #666666);\r\n line-height: 1.4;\r\n margin-bottom: 16px;\r\n\r\n svg {\r\n margin-top: 1px;\r\n color: var(--primary, #0078d4);\r\n }\r\n }\r\n\r\n .link-type-colors {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n\r\n .color-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 6px 10px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n border-radius: 4px;\r\n\r\n .color-preview {\r\n display: flex;\r\n align-items: center;\r\n width: 24px;\r\n }\r\n\r\n .color-label {\r\n font-size: 11px;\r\n font-weight: 700;\r\n color: var(--text-primary, #333333);\r\n min-width: 20px;\r\n }\r\n\r\n .color-desc {\r\n font-size: 11px;\r\n color: var(--text-secondary, #666666);\r\n flex: 1;\r\n }\r\n\r\n .color-input-small {\r\n width: 28px;\r\n height: 24px;\r\n border: 1px solid var(--border, #d0d0d0);\r\n border-radius: 3px;\r\n cursor: pointer;\r\n padding: 0;\r\n\r\n &::-webkit-color-swatch-wrapper {\r\n padding: 2px;\r\n }\r\n\r\n &::-webkit-color-swatch {\r\n border: none;\r\n border-radius: 2px;\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\n.panel-fade-enter-active,\r\n.panel-fade-leave-active {\r\n transition: opacity 0.2s ease;\r\n}\r\n\r\n.panel-fade-enter-from,\r\n.panel-fade-leave-to {\r\n opacity: 0;\r\n}\r\n</style>\r\n","<template>\r\n <div class=\"config-panel-wrapper\">\r\n <button \r\n class=\"config-btn\" \r\n @click=\"togglePanel\"\r\n :class=\"{ active: isOpen }\"\r\n :title=\"t('configPanel.title')\"\r\n >\r\n <div class=\"btn-content\">\r\n <div class=\"btn-icon\">\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z\"/>\r\n </svg>\r\n </div>\r\n <span class=\"btn-text\">{{ t('common.config') }}</span>\r\n </div>\r\n </button>\r\n\r\n <transition name=\"panel-fade\">\r\n <div v-if=\"isOpen\" class=\"config-panel\" @click.stop>\r\n <div class=\"panel-header\">\r\n <h3>{{ t('configPanel.title') }}</h3>\r\n <button class=\"close-btn\" @click=\"closePanel\" :title=\"t('common.close')\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <div class=\"panel-content\">\r\n <!-- 语言配置区域 -->\r\n <div class=\"config-section\">\r\n <h4 class=\"section-title\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z\"/>\r\n </svg>\r\n {{ t('configPanel.languageSettings') }}\r\n </h4>\r\n <div class=\"language-selector-inline\">\r\n <div\r\n v-for=\"locale in locales\"\r\n :key=\"locale.value\"\r\n class=\"language-option\"\r\n :class=\"{ active: currentLocale === locale.value }\"\r\n @click=\"selectLocale(locale.value)\"\r\n >\r\n <span class=\"lang-label\">{{ locale.label }}</span>\r\n <div v-if=\"currentLocale === locale.value\" class=\"lang-check\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/>\r\n </svg>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 主题配置区域 -->\r\n <div class=\"config-section\">\r\n <h4 class=\"section-title\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z\"/>\r\n </svg>\r\n {{ t('configPanel.themeSettings') }}\r\n </h4>\r\n <div class=\"theme-grid\">\r\n <div\r\n v-for=\"theme in themes\"\r\n :key=\"theme.id\"\r\n class=\"theme-card\"\r\n :class=\"{ active: currentTheme === theme.id }\"\r\n @click=\"selectTheme(theme.id)\"\r\n >\r\n <div class=\"theme-preview\" :style=\"{ background: theme.preview }\"></div>\r\n <div class=\"theme-info\">\r\n <div class=\"theme-name\">{{ t(theme.nameKey) }}</div>\r\n <div class=\"theme-desc\">{{ t(theme.descKey) }}</div>\r\n </div>\r\n <div v-if=\"currentTheme === theme.id\" class=\"theme-check\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/>\r\n </svg>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 连线配置区域 -->\r\n <div class=\"config-section\">\r\n <h4 class=\"section-title\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M6 9l6 6 6-6\"/>\r\n <circle cx=\"4\" cy=\"9\" r=\"2\"/>\r\n <circle cx=\"20\" cy=\"9\" r=\"2\"/>\r\n <path d=\"M10 9h4\"/>\r\n </svg>\r\n {{ t('configPanel.linkSettings') }}\r\n </h4>\r\n <div class=\"link-config-content\">\r\n <!-- 说明文字 -->\r\n <div class=\"config-info-box\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" style=\"flex-shrink: 0;\">\r\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/>\r\n </svg>\r\n <span>{{ t('configPanel.linkConfig.info') }}</span>\r\n </div>\r\n\r\n <!-- 路径类型选择 -->\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.pathType') }}</label>\r\n <div class=\"path-type-grid\">\r\n <div \r\n v-for=\"pathType in pathTypes\" \r\n :key=\"pathType.value\"\r\n class=\"path-type-card\"\r\n :class=\"{ active: linkConfig.pathType === pathType.value }\"\r\n @click=\"selectPathType(pathType.value)\"\r\n >\r\n <svg width=\"50\" height=\"30\" viewBox=\"0 0 60 40\">\r\n <path \r\n :d=\"pathType.preview\" \r\n stroke=\"currentColor\" \r\n stroke-width=\"2\" \r\n fill=\"none\"\r\n />\r\n </svg>\r\n <span class=\"path-name\">{{ pathType.name }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 基础样式配置 -->\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.color') }}</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.color\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.width') }}: {{ linkConfig.width }}px</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.width\" \r\n @input=\"updateLinkConfig\"\r\n min=\"1\" \r\n max=\"5\" \r\n step=\"0.5\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.dashStyle') }}</label>\r\n <select v-model=\"linkConfig.dashArray\" @change=\"updateLinkConfig\" class=\"select-input\">\r\n <option :value=\"undefined\">{{ t('configPanel.linkConfig.solid') }}</option>\r\n <option value=\"3,3\">{{ t('configPanel.linkConfig.shortDash') }}</option>\r\n <option value=\"5,5\">{{ t('configPanel.linkConfig.mediumDash') }}</option>\r\n <option value=\"8,4\">{{ t('configPanel.linkConfig.longDash') }}</option>\r\n <option value=\"2,2,8,2\">{{ t('configPanel.linkConfig.dotDash') }}</option>\r\n </select>\r\n </div>\r\n\r\n <!-- 贝塞尔曲线配置 -->\r\n <div v-if=\"linkConfig.pathType === 'bezier'\" class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.curvature') }}: {{ linkConfig.bezierCurvature }}</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.bezierCurvature\" \r\n @input=\"updateLinkConfig\"\r\n min=\"0.1\" \r\n max=\"1\" \r\n step=\"0.1\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n\r\n <!-- 直角连线配置 -->\r\n <template v-if=\"linkConfig.pathType === 'right-angle'\">\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.offset') }}: {{ linkConfig.rightAngleOffset }}px</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.rightAngleOffset\" \r\n @input=\"updateLinkConfig\"\r\n min=\"10\" \r\n max=\"80\" \r\n step=\"5\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"linkConfig.smoothCorners\" \r\n @change=\"updateLinkConfig\"\r\n />\r\n {{ t('configPanel.linkConfig.smoothCorners') }}\r\n </label>\r\n </div>\r\n\r\n <div v-if=\"linkConfig.smoothCorners\" class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.cornerRadius') }}: {{ linkConfig.cornerRadius }}px</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.cornerRadius\" \r\n @input=\"updateLinkConfig\"\r\n min=\"0\" \r\n max=\"15\" \r\n step=\"1\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n </template>\r\n\r\n <!-- 箭头配置 -->\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"linkConfig.showArrow\" \r\n @change=\"updateLinkConfig\"\r\n />\r\n {{ t('configPanel.linkConfig.showArrow') }}\r\n </label>\r\n </div>\r\n\r\n <template v-if=\"linkConfig.showArrow\">\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.arrowSize') }}: {{ linkConfig.arrowSize }}px</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.arrowSize\" \r\n @input=\"updateLinkConfig\"\r\n min=\"4\" \r\n max=\"16\" \r\n step=\"1\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.arrowColor') }}</label>\r\n <div class=\"color-group\">\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.arrowColor\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input\"\r\n />\r\n <button \r\n @click=\"linkConfig.arrowColor = linkConfig.color; updateLinkConfig()\" \r\n class=\"sync-btn\"\r\n :title=\"t('tooltip.syncArrowColor')\"\r\n >\r\n {{ t('configPanel.linkConfig.syncColor') }}\r\n </button>\r\n </div>\r\n </div>\r\n </template>\r\n\r\n <!-- 虚线动画 -->\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"linkConfig.enableDashAnimation\" \r\n @change=\"updateLinkConfig\"\r\n />\r\n {{ t('configPanel.linkConfig.dashAnimation') }}\r\n </label>\r\n </div>\r\n\r\n <div v-if=\"linkConfig.enableDashAnimation\" class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.animationSpeed') }}: {{ linkConfig.dashAnimationSpeed }}s</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.dashAnimationSpeed\" \r\n @input=\"updateLinkConfig\"\r\n min=\"0.2\" \r\n max=\"3\" \r\n step=\"0.2\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n\r\n <!-- 标签配置 -->\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"linkConfig.showLabels\" \r\n @change=\"updateLinkConfig\"\r\n />\r\n {{ t('configPanel.linkConfig.showLabels') }}\r\n </label>\r\n </div>\r\n\r\n <template v-if=\"linkConfig.showLabels\">\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.labelColor') }}</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.labelColor\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.fontSize') }}: {{ linkConfig.labelFontSize }}px</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.labelFontSize\" \r\n @input=\"updateLinkConfig\"\r\n min=\"8\" \r\n max=\"16\" \r\n step=\"1\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n </template>\r\n\r\n <!-- 连线类型颜色配置 -->\r\n <div class=\"config-subsection\">\r\n <h5 class=\"subsection-title\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" style=\"margin-right: 4px;\">\r\n <path d=\"M19 15l-6 6-1.42-1.42L15.17 16H4V4h2v10h9.17l-3.59-3.58L13 9l6 6z\"/>\r\n </svg>\r\n {{ t('configPanel.linkConfig.typeColors') }}\r\n </h5>\r\n \r\n <div class=\"link-type-colors\">\r\n <div class=\"color-item\">\r\n <div class=\"color-preview\">\r\n <svg width=\"20\" height=\"10\" viewBox=\"0 0 20 10\">\r\n <line x1=\"0\" y1=\"5\" x2=\"14\" y2=\"5\" :stroke=\"linkConfig.linkTypeColors.finishToStart\" stroke-width=\"2\"/>\r\n <polygon :points=\"'20,5 14,2 14,8'\" :fill=\"linkConfig.linkTypeColors.finishToStart\"/>\r\n </svg>\r\n </div>\r\n <span class=\"color-label\">{{ t('link.fs') }}</span>\r\n <span class=\"color-desc\">{{ t('link.finishToStart') }}</span>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.linkTypeColors.finishToStart\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input-small\"\r\n />\r\n </div>\r\n \r\n <div class=\"color-item\">\r\n <div class=\"color-preview\">\r\n <svg width=\"20\" height=\"10\" viewBox=\"0 0 20 10\">\r\n <line x1=\"0\" y1=\"5\" x2=\"14\" y2=\"5\" :stroke=\"linkConfig.linkTypeColors.startToStart\" stroke-width=\"2\"/>\r\n <polygon :points=\"'20,5 14,2 14,8'\" :fill=\"linkConfig.linkTypeColors.startToStart\"/>\r\n </svg>\r\n </div>\r\n <span class=\"color-label\">{{ t('link.ss') }}</span>\r\n <span class=\"color-desc\">{{ t('link.startToStart') }}</span>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.linkTypeColors.startToStart\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input-small\"\r\n />\r\n </div>\r\n \r\n <div class=\"color-item\">\r\n <div class=\"color-preview\">\r\n <svg width=\"20\" height=\"10\" viewBox=\"0 0 20 10\">\r\n <line x1=\"0\" y1=\"5\" x2=\"14\" y2=\"5\" :stroke=\"linkConfig.linkTypeColors.finishToFinish\" stroke-width=\"2\"/>\r\n <polygon :points=\"'20,5 14,2 14,8'\" :fill=\"linkConfig.linkTypeColors.finishToFinish\"/>\r\n </svg>\r\n </div>\r\n <span class=\"color-label\">{{ t('link.ff') }}</span>\r\n <span class=\"color-desc\">{{ t('link.finishToFinish') }}</span>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.linkTypeColors.finishToFinish\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input-small\"\r\n />\r\n </div>\r\n \r\n <div class=\"color-item\">\r\n <div class=\"color-preview\">\r\n <svg width=\"20\" height=\"10\" viewBox=\"0 0 20 10\">\r\n <line x1=\"0\" y1=\"5\" x2=\"14\" y2=\"5\" :stroke=\"linkConfig.linkTypeColors.startToFinish\" stroke-width=\"2\"/>\r\n <polygon :points=\"'20,5 14,2 14,8'\" :fill=\"linkConfig.linkTypeColors.startToFinish\"/>\r\n </svg>\r\n </div>\r\n <span class=\"color-label\">{{ t('link.sf') }}</span>\r\n <span class=\"color-desc\">{{ t('link.startToFinish') }}</span>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.linkTypeColors.startToFinish\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input-small\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 父子关系样式 -->\r\n <div class=\"config-subsection\">\r\n <h5 class=\"subsection-title\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" style=\"margin-right: 4px;\">\r\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/>\r\n </svg>\r\n {{ t('configPanel.linkConfig.parentChildStyle') }}\r\n </h5>\r\n <div class=\"config-info-box\" style=\"margin-bottom: 12px;\">\r\n <span style=\"font-size: 11px;\">{{ t('configPanel.linkConfig.parentChildInfo') }}</span>\r\n </div>\r\n \r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.color') }}</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"linkConfig.parentChildStyle.color\" \r\n @change=\"updateLinkConfig\"\r\n class=\"color-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.width') }}: {{ linkConfig.parentChildStyle.width }}px</label>\r\n <input \r\n type=\"range\" \r\n v-model.number=\"linkConfig.parentChildStyle.width\" \r\n @input=\"updateLinkConfig\"\r\n min=\"1\" \r\n max=\"3\" \r\n step=\"0.5\"\r\n class=\"range-input\"\r\n />\r\n </div>\r\n\r\n <div class=\"config-group\">\r\n <label class=\"config-label\">{{ t('configPanel.linkConfig.dashStyle') }}</label>\r\n <select v-model=\"linkConfig.parentChildStyle.dashArray\" @change=\"updateLinkConfig\" class=\"select-input\">\r\n <option :value=\"undefined\">{{ t('configPanel.linkConfig.solid') }}</option>\r\n <option value=\"3,3\">{{ t('configPanel.linkConfig.shortDash') }}</option>\r\n <option value=\"5,5\">{{ t('configPanel.linkConfig.mediumDash') }}</option>\r\n <option value=\"8,4\">{{ t('configPanel.linkConfig.longDash') }}</option>\r\n </select>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </transition>\r\n\r\n <div v-if=\"isOpen\" class=\"panel-overlay\" @click=\"closePanel\"></div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, onMounted, watch, inject, computed, type Ref } from 'vue';\r\nimport { ganttThemes, ganttThemeManager, type GanttTheme } from './themes/GanttThemes';\r\nimport { useLinkConfig, LinkPathType } from './LinkConfig';\r\nimport { useI18n, type Locale } from './i18n';\r\n\r\nexport default defineComponent({\r\n name: 'GanttConfigPanel',\r\n setup() {\r\n const { t, locale, setLocale, getLocales } = useI18n();\r\n const isOpen = ref(false);\r\n const themes = ref<GanttTheme[]>(ganttThemes);\r\n const currentTheme = ref<string>('metro');\r\n \r\n // 语言选择器相关\r\n const currentLocale = computed(() => locale.value);\r\n const locales = computed(() => getLocales());\r\n \r\n const selectLocale = (localeValue: Locale) => {\r\n setLocale(localeValue);\r\n };\r\n \r\n // 注入甘特图容器引用\r\n const ganttContainer = inject<Ref<HTMLElement | undefined>>('ganttContainer');\r\n\r\n // 使用连线配置管理器\r\n const { config: linkConfigManager, updateConfig: updateLinkConfigManager } = useLinkConfig();\r\n\r\n // 连线配置 - 从管理器初始化\r\n const linkConfig = ref({\r\n pathType: linkConfigManager.pathType || LinkPathType.BEZIER,\r\n color: linkConfigManager.color || '#3498db',\r\n width: linkConfigManager.width || 2,\r\n dashArray: linkConfigManager.dashArray,\r\n bezierCurvature: linkConfigManager.bezierCurvature || 0.5,\r\n rightAngleOffset: linkConfigManager.rightAngleOffset || 40,\r\n smoothCorners: linkConfigManager.smoothCorners || false,\r\n cornerRadius: linkConfigManager.cornerRadius || 5,\r\n showArrow: linkConfigManager.showArrow !== undefined ? linkConfigManager.showArrow : true,\r\n arrowSize: linkConfigManager.arrowSize || 8,\r\n arrowColor: linkConfigManager.arrowColor || '#3498db',\r\n enableDashAnimation: linkConfigManager.enableDashAnimation || false,\r\n dashAnimationSpeed: linkConfigManager.dashAnimationSpeed || 1,\r\n showLabels: linkConfigManager.showLabels || false,\r\n labelColor: linkConfigManager.labelColor || '#333333',\r\n labelFontSize: linkConfigManager.labelFontSize || 12,\r\n parentChildStyle: {\r\n color: linkConfigManager.parentChildStyle?.color || '#999999',\r\n width: linkConfigManager.parentChildStyle?.width || 1.5,\r\n dashArray: linkConfigManager.parentChildStyle?.dashArray || '3,3'\r\n },\r\n linkTypeColors: {\r\n finishToStart: linkConfigManager.linkTypeColors?.finishToStart || '#3498db',\r\n startToStart: linkConfigManager.linkTypeColors?.startToStart || '#2ecc71',\r\n finishToFinish: linkConfigManager.linkTypeColors?.finishToFinish || '#e74c3c',\r\n startToFinish: linkConfigManager.linkTypeColors?.startToFinish || '#f39c12'\r\n },\r\n linkTypeVisibility: {\r\n finishToStart: linkConfigManager.linkTypeVisibility?.finishToStart ?? true,\r\n startToStart: linkConfigManager.linkTypeVisibility?.startToStart ?? true,\r\n finishToFinish: linkConfigManager.linkTypeVisibility?.finishToFinish ?? true,\r\n startToFinish: linkConfigManager.linkTypeVisibility?.startToFinish ?? true,\r\n parentChild: linkConfigManager.linkTypeVisibility?.parentChild ?? true\r\n }\r\n });\r\n\r\n // 路径类型 - 使用 computed 以响应语言变化\r\n const pathTypes = computed(() => [\r\n { value: LinkPathType.STRAIGHT, name: t('configPanel.linkConfig.straight'), preview: 'M 10 20 L 50 20' },\r\n { value: LinkPathType.BEZIER, name: t('configPanel.linkConfig.bezier'), preview: 'M 10 20 C 25 20 35 20 50 20' },\r\n { value: LinkPathType.RIGHT_ANGLE, name: t('configPanel.linkConfig.rightAngle'), preview: 'M 10 20 L 30 20 L 30 30 L 50 30' }\r\n ]);\r\n\r\n const togglePanel = () => {\r\n isOpen.value = !isOpen.value;\r\n };\r\n\r\n const closePanel = () => {\r\n isOpen.value = false;\r\n };\r\n\r\n const selectTheme = (themeId: string) => {\r\n currentTheme.value = themeId;\r\n ganttThemeManager.setTheme(themeId);\r\n };\r\n\r\n const selectPathType = (pathType: string) => {\r\n linkConfig.value.pathType = pathType as LinkPathType;\r\n updateLinkConfig();\r\n };\r\n\r\n const updateLinkConfig = () => {\r\n // 更新到连线配置管理器\r\n updateLinkConfigManager(linkConfig.value);\r\n console.log('Link config updated:', linkConfig.value);\r\n };\r\n\r\n onMounted(() => {\r\n // 设置甘特图容器到主题管理器\r\n if (ganttContainer?.value) {\r\n console.log('GanttConfigPanel: Setting gantt container to theme manager');\r\n ganttThemeManager.setGanttContainer(ganttContainer.value);\r\n } else {\r\n console.warn('GanttConfigPanel: ganttContainer not available');\r\n }\r\n \r\n currentTheme.value = ganttThemeManager.getCurrentTheme();\r\n });\r\n\r\n // 监听linkConfig的深层变化\r\n watch(linkConfig, (newConfig) => {\r\n updateLinkConfigManager(newConfig);\r\n }, { deep: true });\r\n\r\n return {\r\n t,\r\n isOpen,\r\n themes,\r\n currentTheme,\r\n linkConfig,\r\n pathTypes,\r\n currentLocale,\r\n locales,\r\n togglePanel,\r\n closePanel,\r\n selectTheme,\r\n selectPathType,\r\n selectLocale,\r\n updateLinkConfig\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.config-panel-wrapper {\r\n position: relative;\r\n}\r\n\r\n.config-btn {\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border: 1px solid var(--border, #d0d0d0);\r\n box-shadow: var(--shadow-inset, inset 0 1px 0 rgba(255, 255, 255, 0.8));\r\n padding: 8px 16px;\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n font-family: var(--font-family);\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n\r\n &:hover {\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n }\r\n\r\n &:active, &.active {\r\n background: var(--bg-active, linear-gradient(145deg, #0078d4, #106ebe));\r\n \r\n .btn-icon, .btn-text {\r\n color: var(--text-white, #ffffff);\r\n }\r\n }\r\n\r\n .btn-content {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n }\r\n\r\n .btn-icon {\r\n color: var(--text-secondary, #666666);\r\n display: flex;\r\n align-items: center;\r\n transition: color var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n .btn-text {\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--text-primary, #333333);\r\n transition: color var(--transition-fast, 0.15s ease);\r\n }\r\n}\r\n\r\n.panel-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: rgba(0, 0, 0, 0.3);\r\n z-index: 999;\r\n}\r\n\r\n.config-panel {\r\n position: fixed;\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%, -50%);\r\n width: 90%;\r\n max-width: 1000px;\r\n max-height: 85vh;\r\n background: var(--bg-content, #ffffff);\r\n border: 1px solid var(--border, #d0d0d0);\r\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\r\n z-index: 1000;\r\n display: flex;\r\n flex-direction: column;\r\n font-family: var(--font-family);\r\n}\r\n\r\n.panel-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 20px 24px;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border-bottom: 1px solid var(--border, #d0d0d0);\r\n\r\n h3 {\r\n margin: 0;\r\n font-size: 18px;\r\n font-weight: 600;\r\n color: var(--text-primary, #333333);\r\n }\r\n\r\n .close-btn {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 4px;\r\n color: var(--text-secondary, #666666);\r\n transition: all var(--transition-fast, 0.15s ease);\r\n display: flex;\r\n align-items: center;\r\n\r\n &:hover {\r\n color: var(--primary, #0078d4);\r\n transform: scale(1.1);\r\n }\r\n }\r\n}\r\n\r\n.panel-content {\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 24px;\r\n display: grid;\r\n grid-template-columns: 1fr 1fr;\r\n gap: 24px;\r\n}\r\n\r\n.config-section {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n\r\n .section-title {\r\n margin: 0;\r\n font-size: 14px;\r\n font-weight: 600;\r\n color: var(--text-primary, #333333);\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding-bottom: 12px;\r\n border-bottom: 2px solid var(--border, #d0d0d0);\r\n\r\n svg {\r\n color: var(--primary, #0078d4);\r\n }\r\n }\r\n}\r\n\r\n.theme-grid {\r\n display: grid;\r\n grid-template-columns: 1fr;\r\n gap: 12px;\r\n}\r\n\r\n.theme-card {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n padding: 12px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 2px solid var(--border, #d0d0d0);\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n position: relative;\r\n\r\n &:hover {\r\n border-color: var(--primary, #0078d4);\r\n transform: translateY(-2px);\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\r\n }\r\n\r\n &.active {\r\n border-color: var(--primary, #0078d4);\r\n background: var(--bg-content, #ffffff);\r\n box-shadow: 0 0 0 2px var(--primary, #0078d4), 0 2px 8px rgba(0, 120, 212, 0.15);\r\n \r\n .theme-info {\r\n .theme-name {\r\n color: var(--primary, #0078d4);\r\n }\r\n \r\n .theme-desc {\r\n color: var(--text-primary, #333333);\r\n }\r\n }\r\n }\r\n\r\n .theme-preview {\r\n width: 40px;\r\n height: 40px;\r\n border-radius: 4px;\r\n flex-shrink: 0;\r\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);\r\n }\r\n\r\n .theme-info {\r\n flex: 1;\r\n min-width: 0;\r\n\r\n .theme-name {\r\n font-size: 13px;\r\n font-weight: 600;\r\n color: var(--text-primary, #333333);\r\n margin-bottom: 4px;\r\n }\r\n\r\n .theme-desc {\r\n font-size: 11px;\r\n color: var(--text-secondary, #666666);\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n }\r\n }\r\n\r\n .theme-check {\r\n color: var(--primary, #0078d4);\r\n flex-shrink: 0;\r\n }\r\n}\r\n\r\n.language-selector-inline {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\r\n gap: 8px;\r\n \r\n .language-option {\r\n padding: 10px 12px;\r\n border: 1px solid var(--border, #d0d0d0);\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n box-shadow: var(--shadow-inset, inset 0 1px 0 rgba(255, 255, 255, 0.8));\r\n\r\n &:hover {\r\n border-color: var(--primary, #0078d4);\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n transform: translateY(-1px);\r\n }\r\n\r\n &.active {\r\n border-color: var(--primary, #0078d4);\r\n background: var(--bg-content, #ffffff);\r\n box-shadow: 0 0 0 2px var(--primary, #0078d4), 0 2px 8px rgba(0, 120, 212, 0.15);\r\n \r\n .lang-label {\r\n color: var(--primary, #0078d4);\r\n font-weight: 600;\r\n }\r\n }\r\n\r\n .lang-label {\r\n font-size: 12px;\r\n flex: 1;\r\n color: var(--text-primary, #333333);\r\n }\r\n\r\n .lang-check {\r\n color: var(--primary, #0078d4);\r\n display: flex;\r\n align-items: center;\r\n margin-left: 8px;\r\n }\r\n }\r\n}\r\n\r\n.link-config-content {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n\r\n .config-group {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n\r\n .config-label {\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--text-primary, #333333);\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n }\r\n\r\n .color-input {\r\n width: 60px;\r\n height: 36px;\r\n border: 1px solid var(--border, #d0d0d0);\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n\r\n &:hover {\r\n border-color: var(--primary, #0078d4);\r\n }\r\n }\r\n\r\n .range-input {\r\n width: 100%;\r\n height: 6px;\r\n -webkit-appearance: none;\r\n appearance: none;\r\n background: var(--bg-secondary, #e8e8e8);\r\n outline: none;\r\n border-radius: 3px;\r\n\r\n &::-webkit-slider-thumb {\r\n -webkit-appearance: none;\r\n appearance: none;\r\n width: 16px;\r\n height: 16px;\r\n background: var(--primary, #0078d4);\r\n cursor: pointer;\r\n border-radius: 50%;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n\r\n &:hover {\r\n transform: scale(1.2);\r\n }\r\n }\r\n\r\n &::-moz-range-thumb {\r\n width: 16px;\r\n height: 16px;\r\n background: var(--primary, #0078d4);\r\n cursor: pointer;\r\n border-radius: 50%;\r\n border: none;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n\r\n &:hover {\r\n transform: scale(1.2);\r\n }\r\n }\r\n }\r\n }\r\n\r\n .path-type-grid {\r\n display: grid;\r\n grid-template-columns: repeat(3, 1fr);\r\n gap: 8px;\r\n }\r\n\r\n .path-type-card {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 12px 8px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 2px solid var(--border, #d0d0d0);\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n color: var(--text-secondary, #666666);\r\n\r\n &:hover {\r\n border-color: var(--primary, #0078d4);\r\n color: var(--primary, #0078d4);\r\n }\r\n\r\n &.active {\r\n border-color: var(--primary, #0078d4);\r\n background: var(--bg-content, #ffffff);\r\n box-shadow: 0 0 0 2px var(--primary, #0078d4), 0 2px 8px rgba(0, 120, 212, 0.15);\r\n \r\n .path-name {\r\n color: var(--primary, #0078d4);\r\n }\r\n }\r\n\r\n .path-name {\r\n font-size: 11px;\r\n font-weight: 600;\r\n }\r\n }\r\n\r\n .select-input {\r\n width: 100%;\r\n padding: 8px 12px;\r\n border: 1px solid var(--border, #d0d0d0);\r\n background: var(--bg-content, #ffffff);\r\n color: var(--text-primary, #333333);\r\n font-size: 12px;\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n\r\n &:hover {\r\n border-color: var(--primary, #0078d4);\r\n }\r\n\r\n &:focus {\r\n outline: none;\r\n border-color: var(--primary, #0078d4);\r\n box-shadow: 0 0 0 2px rgba(0, 120, 212, 0.1);\r\n }\r\n }\r\n\r\n .color-group {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n }\r\n\r\n .sync-btn {\r\n padding: 6px 12px;\r\n font-size: 11px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n color: var(--text-primary, #333333);\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n white-space: nowrap;\r\n\r\n &:hover {\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border-color: var(--primary, #0078d4);\r\n }\r\n\r\n &:active {\r\n transform: scale(0.95);\r\n }\r\n }\r\n\r\n .config-subsection {\r\n margin-top: 16px;\r\n padding-top: 16px;\r\n border-top: 1px solid var(--border, #d0d0d0);\r\n\r\n .subsection-title {\r\n margin: 0 0 12px 0;\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--text-secondary, #666666);\r\n display: flex;\r\n align-items: center;\r\n }\r\n }\r\n\r\n .config-info-box {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 8px;\r\n padding: 8px 12px;\r\n background: var(--bg-secondary, #f8f9fa);\r\n border-left: 3px solid var(--primary, #0078d4);\r\n font-size: 11px;\r\n color: var(--text-secondary, #666666);\r\n line-height: 1.4;\r\n margin-bottom: 16px;\r\n\r\n svg {\r\n margin-top: 1px;\r\n color: var(--primary, #0078d4);\r\n }\r\n }\r\n\r\n .link-type-colors {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n\r\n .color-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 6px 10px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n border-radius: 4px;\r\n\r\n .color-preview {\r\n display: flex;\r\n align-items: center;\r\n width: 24px;\r\n }\r\n\r\n .color-label {\r\n font-size: 11px;\r\n font-weight: 700;\r\n color: var(--text-primary, #333333);\r\n min-width: 20px;\r\n }\r\n\r\n .color-desc {\r\n font-size: 11px;\r\n color: var(--text-secondary, #666666);\r\n flex: 1;\r\n }\r\n\r\n .color-input-small {\r\n width: 28px;\r\n height: 24px;\r\n border: 1px solid var(--border, #d0d0d0);\r\n border-radius: 3px;\r\n cursor: pointer;\r\n padding: 0;\r\n\r\n &::-webkit-color-swatch-wrapper {\r\n padding: 2px;\r\n }\r\n\r\n &::-webkit-color-swatch {\r\n border: none;\r\n border-radius: 2px;\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\n.panel-fade-enter-active,\r\n.panel-fade-leave-active {\r\n transition: opacity 0.2s ease;\r\n}\r\n\r\n.panel-fade-enter-from,\r\n.panel-fade-leave-to {\r\n opacity: 0;\r\n}\r\n</style>\r\n","import { z } from 'zod';\r\n \r\n// 定义 Zod Schema \r\nexport const ConfirmDateDataSchema = z.object({ \r\n date: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/), // 格式如 YYYY-MM-DD \r\n year: z.number().int(), \r\n month: z.number().int().min(1).max(12), \r\n week: z.number().int().min(1).max(53), \r\n monthStr: z.string(), \r\n weekStr: z.string(), \r\n day: z.number().int().min(1).max(31), \r\n});\r\n // 生成 TypeScript 类型 \r\nexport type ConfirmDateData = z.infer<typeof ConfirmDateDataSchema>;","<template>\r\n <div class=\"page gantt-container\" ref=\"ganttContainer\">\r\n <div class=\"toolbar\">\r\n <div class=\"dateInput\">\r\n <DatePicker :date=\"startDate\" :min-date=\"minStartDate\" :max-date=\"maxStartDate\"\r\n @confirm=\"confirmStart\" />\r\n <span style=\"margin-right:20px;margin-left:20px;color:#606266\">{{ t('common.to') }}</span>\r\n <DatePicker :date=\"endDate\" :min-date=\"minEndDate\" :max-date=\"maxEndDate\" @confirm=\"confirmEnd\" />\r\n </div>\r\n <div class=\"buttonGroup\">\r\n <div :class=\"buttonClass[0]\" class=\"metro-btn\" @click=\"timeMode('月')\">\r\n <div class=\"metro-content\">\r\n <div class=\"metro-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z\"/>\r\n </svg>\r\n </div>\r\n <div class=\"metro-text\">{{ t('viewMode.month') }}</div>\r\n </div>\r\n </div>\r\n <div :class=\"buttonClass[1]\" class=\"metro-btn\" @click=\"timeMode('周')\">\r\n <div class=\"metro-content\">\r\n <div class=\"metro-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z\"/>\r\n </svg>\r\n </div>\r\n <div class=\"metro-text\">{{ t('viewMode.week') }}</div>\r\n </div>\r\n </div>\r\n <div :class=\"buttonClass[2]\" class=\"metro-btn\" @click=\"timeMode('日')\">\r\n <div class=\"metro-content\">\r\n <div class=\"metro-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 2 2h8c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 18H6V4h8v16z\"/>\r\n </svg>\r\n </div>\r\n <div class=\"metro-text\">{{ t('viewMode.day') }}</div>\r\n </div>\r\n </div>\r\n <div :class=\"buttonClass[3]\" class=\"metro-btn\" @click=\"timeMode('时')\">\r\n <div class=\"metro-content\">\r\n <div class=\"metro-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z\"/>\r\n </svg>\r\n </div>\r\n <div class=\"metro-text\">{{ t('viewMode.hour') }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n <!-- 连线类型图例 -->\r\n <div class=\"link-legend\">\r\n <div class=\"legend-title\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M19 15l-6 6-1.42-1.42L15.17 16H4V4h2v10h9.17l-3.59-3.58L13 9l6 6z\"/>\r\n </svg>\r\n {{ t('link.legend') }}\r\n </div>\r\n <div class=\"legend-items\">\r\n <label class=\"legend-item\" :class=\"{ disabled: !linkTypeVisibility.parentChild }\" title=\"父子关系:显示任务的层级结构\">\r\n <input type=\"checkbox\" v-model=\"linkTypeVisibility.parentChild\" @change=\"updateLinkVisibility\" />\r\n <svg width=\"24\" height=\"12\" viewBox=\"0 0 24 12\">\r\n <line x1=\"0\" y1=\"6\" x2=\"18\" y2=\"6\" stroke=\"#95a5a6\" stroke-width=\"1.5\" stroke-dasharray=\"3,3\"/>\r\n <polygon points=\"24,6 18,3 18,9\" fill=\"#95a5a6\"/>\r\n </svg>\r\n <span class=\"legend-label\">{{ t('link.pc') }}</span>\r\n <span class=\"legend-desc\">{{ t('link.parentChild') }}</span>\r\n </label>\r\n <label class=\"legend-item\" :class=\"{ disabled: !linkTypeVisibility.finishToStart }\" title=\"完成-开始:前置任务完成后,后续任务才能开始\">\r\n <input type=\"checkbox\" v-model=\"linkTypeVisibility.finishToStart\" @change=\"updateLinkVisibility\" />\r\n <svg width=\"24\" height=\"12\" viewBox=\"0 0 24 12\">\r\n <line x1=\"0\" y1=\"6\" x2=\"18\" y2=\"6\" :stroke=\"linkTypeColors.finishToStart\" stroke-width=\"2\"/>\r\n <polygon points=\"24,6 18,3 18,9\" :fill=\"linkTypeColors.finishToStart\"/>\r\n </svg>\r\n <span class=\"legend-label\">{{ t('link.fs') }}</span>\r\n <span class=\"legend-desc\">{{ t('link.finishToStart') }}</span>\r\n </label>\r\n <label class=\"legend-item\" :class=\"{ disabled: !linkTypeVisibility.startToStart }\" title=\"开始-开始:两个任务同时开始\">\r\n <input type=\"checkbox\" v-model=\"linkTypeVisibility.startToStart\" @change=\"updateLinkVisibility\" />\r\n <svg width=\"24\" height=\"12\" viewBox=\"0 0 24 12\">\r\n <line x1=\"0\" y1=\"6\" x2=\"18\" y2=\"6\" :stroke=\"linkTypeColors.startToStart\" stroke-width=\"2\"/>\r\n <polygon points=\"24,6 18,3 18,9\" :fill=\"linkTypeColors.startToStart\"/>\r\n </svg>\r\n <span class=\"legend-label\">{{ t('link.ss') }}</span>\r\n <span class=\"legend-desc\">{{ t('link.startToStart') }}</span>\r\n </label>\r\n <label class=\"legend-item\" :class=\"{ disabled: !linkTypeVisibility.finishToFinish }\" title=\"完成-完成:两个任务同时完成\">\r\n <input type=\"checkbox\" v-model=\"linkTypeVisibility.finishToFinish\" @change=\"updateLinkVisibility\" />\r\n <svg width=\"24\" height=\"12\" viewBox=\"0 0 24 12\">\r\n <line x1=\"0\" y1=\"6\" x2=\"18\" y2=\"6\" :stroke=\"linkTypeColors.finishToFinish\" stroke-width=\"2\"/>\r\n <polygon points=\"24,6 18,3 18,9\" :fill=\"linkTypeColors.finishToFinish\"/>\r\n </svg>\r\n <span class=\"legend-label\">{{ t('link.ff') }}</span>\r\n <span class=\"legend-desc\">{{ t('link.finishToFinish') }}</span>\r\n </label>\r\n <label class=\"legend-item\" :class=\"{ disabled: !linkTypeVisibility.startToFinish }\" title=\"开始-完成:前置任务开始后,后续任务才能完成\">\r\n <input type=\"checkbox\" v-model=\"linkTypeVisibility.startToFinish\" @change=\"updateLinkVisibility\" />\r\n <svg width=\"24\" height=\"12\" viewBox=\"0 0 24 12\">\r\n <line x1=\"0\" y1=\"6\" x2=\"18\" y2=\"6\" :stroke=\"linkTypeColors.startToFinish\" stroke-width=\"2\"/>\r\n <polygon points=\"24,6 18,3 18,9\" :fill=\"linkTypeColors.startToFinish\"/>\r\n </svg>\r\n <span class=\"legend-label\">{{ t('link.sf') }}</span>\r\n <span class=\"legend-desc\">{{ t('link.startToFinish') }}</span>\r\n </label>\r\n </div>\r\n </div>\r\n <div class=\"config-buttons\">\r\n <GanttConfigPanel />\r\n </div>\r\n </div>\r\n <div class=\"gantt\">\r\n <SplitPane direction=\"row\" :min=\"0\" :max=\"100\" :triggerLength=\"10\"\r\n v-model:paneLengthPercent=\"paneLengthPercent\">\r\n <template #one>\r\n <TaskTable :headersHeight='styleConfig.headersHeight' :rowHeight='styleConfig.rowHeight'>\r\n </TaskTable>\r\n </template>\r\n <template #two>\r\n <RightTable ref='barContent' :headersHeight='styleConfig.headersHeight'\r\n :rowHeight='styleConfig.rowHeight'></RightTable>\r\n </template>\r\n </SplitPane>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { ref, defineComponent, computed, provide, onBeforeMount, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';\r\nimport dayjs from 'dayjs';\r\nimport customParseFormat from 'dayjs/plugin/customParseFormat';\r\nimport 'dayjs/locale/zh-cn';\r\nimport 'dayjs/locale/en';\r\nimport 'dayjs/locale/ja';\r\nimport 'dayjs/locale/ko';\r\nimport 'dayjs/locale/fr';\r\nimport 'dayjs/locale/de';\r\nimport 'dayjs/locale/es';\r\nimport 'dayjs/locale/ru';\r\ndayjs.extend(customParseFormat);\r\nimport { Symbols } from './Symbols';\r\nimport { linkDataManager, useLinkConfig } from './LinkConfig';\r\nimport { useI18n } from './i18n';\r\n// 导入日期选择器组件\r\nimport DatePicker from './DatePicker.vue';\r\n// 导入分割面板组件\r\nimport SplitPane from './SplitPane.vue';\r\nimport TaskTable from '../gantt/task/TaskTable.vue';\r\nimport RightTable from './RightTable.vue';\r\nimport GanttConfigPanel from './GanttConfigPanel.vue';\r\nimport { store, mutations } from './Store';\r\nexport type { DataConfig, StyleConfig, EventConfig, TaskHeader } from './Types';\r\n// 移除未使用的类型导入\r\nimport { type ConfirmDateData } from './ZodSchema';\r\n\r\n// 定义月份表头类型\r\ntype MonthHeaders = {\r\n title: string;\r\n width: number;\r\n}\r\n\r\n// 定义天表头类型\r\ntype DayHeaders = MonthHeaders & {\r\n fulldate: string;\r\n}\r\n\r\n// 定义周表头类型\r\ntype WeekHeaders = MonthHeaders & {\r\n fulldate: string;\r\n}\r\n\r\n// 定义小时表头类型\r\ntype HourHeaders = {\r\n title: string;\r\n width: number;\r\n}\r\n\r\n/**\r\n * Gantt 组件,用于展示甘特图。\r\n * 该组件接收样式配置、数据配置和事件配置作为属性。\r\n */\r\nexport default defineComponent({\r\n // 组件名称\r\n name: 'Gantt',\r\n props: {\r\n /**\r\n * 样式配置对象,包含表头高度和行高。\r\n * @type {{ headersHeight: number; rowHeight: number }}\r\n * @required\r\n * @default { headersHeight: 100, rowHeight: 60 }\r\n */\r\n styleConfig: {\r\n type: Object as () => {\r\n headersHeight: number;\r\n rowHeight: number;\r\n setBarColor: (row: Record<string, any>) => string;\r\n },\r\n required: true,\r\n default: () => ({\r\n // 表头高度\r\n headersHeight: 100,\r\n // 行高\r\n rowHeight: 60\r\n }),\r\n // 验证参数合法性\r\n validator: (value: { headersHeight: number; rowHeight: number }) => {\r\n return value.headersHeight > 0 && value.rowHeight > 0;\r\n }\r\n },\r\n /**\r\n * 数据配置对象,包含数据源、任务表头和字段映射函数。\r\n * @type {{ dataSource: any[]; taskHeaders: () => any[]; mapFields: () => any }}\r\n * @required\r\n * @default { dataSource: [], taskHeaders: () => [], mapFields: () => {} }\r\n */\r\n dataConfig: {\r\n type: Object as () => {\r\n dataSource: any[];\r\n taskHeaders: any[];\r\n mapFields: Record<string, any>;\r\n queryStartDate: string;\r\n queryEndDate: string;\r\n dependencies?: Omit<import('./Types').TaskDependency, 'id'>[];\r\n },\r\n required: true,\r\n default: () => ({\r\n dataSource: [],\r\n taskHeaders: [],\r\n mapFields: {}\r\n }),\r\n validator: (value: { dataSource: any[]; taskHeaders: any[]; mapFields: Record<string, any> }) => {\r\n return Array.isArray(value.dataSource) &&\r\n Array.isArray(value.taskHeaders) &&\r\n typeof value.mapFields === 'object' && value.mapFields !== null;\r\n }\r\n },\r\n /**\r\n * 事件配置对象,包含添加根任务、添加子任务、移除任务等事件处理函数。\r\n * @type {{\r\n * addRootTask: () => void;\r\n * addSubTask: (task: any) => void;\r\n * removeTask: (task: any) => void;\r\n * queryTask: (startDate: string, endDate: string, mode: string) => void;\r\n * barDate: (id: any, startDate: string, endDate: string) => void;\r\n * allowChangeTaskDate: (allow: boolean) => void;\r\n * }}\r\n * @required\r\n */\r\n eventConfig: {\r\n type: Object as () => {\r\n addRootTask: (row: Record<string, any> | null) => void;\r\n addSubTask: (task: any) => void;\r\n removeTask: (task: any) => void;\r\n editTask: (task: any) => void;\r\n queryTask: (startDate: string, endDate: string, mode: string) => void;\r\n barDate: (id: any, startDate: string, endDate: string) => void;\r\n allowChangeTaskDate: (allow: boolean) => void;\r\n updateProgress?: (detail: { taskId: any; oldProgress: number; newProgress: number; task: Record<string, any> }) => void;\r\n },\r\n required: true\r\n }\r\n },\r\n components: {\r\n DatePicker,\r\n SplitPane,\r\n TaskTable,\r\n RightTable,\r\n GanttConfigPanel\r\n },\r\n setup(props) {\r\n // 国际化\r\n const { t, locale } = useI18n();\r\n \r\n // 根据当前语言设置 dayjs locale\r\n const getDayjsLocale = () => {\r\n const localeMap: Record<string, string> = {\r\n 'zh-CN': 'zh-cn',\r\n 'en-US': 'en',\r\n 'ja-JP': 'ja',\r\n 'ko-KR': 'ko',\r\n 'fr-FR': 'fr',\r\n 'de-DE': 'de',\r\n 'es-ES': 'es',\r\n 'ru-RU': 'ru'\r\n };\r\n return localeMap[locale.value] || 'en';\r\n };\r\n \r\n // 缓存 mapFields 的结果\r\n const mapFields = computed(() => props.dataConfig.mapFields);\r\n // 缓存 dataSource 的结果\r\n const dataSource = computed(() => props.dataConfig.dataSource);\r\n\r\n // 连线配置\r\n const { config: linkConfig, updateConfig: updateLinkConfig } = useLinkConfig();\r\n\r\n // 连线类型显示控制\r\n const linkTypeVisibility = ref({\r\n finishToStart: linkConfig.linkTypeVisibility?.finishToStart ?? true,\r\n startToStart: linkConfig.linkTypeVisibility?.startToStart ?? true,\r\n finishToFinish: linkConfig.linkTypeVisibility?.finishToFinish ?? true,\r\n startToFinish: linkConfig.linkTypeVisibility?.startToFinish ?? true,\r\n parentChild: linkConfig.linkTypeVisibility?.parentChild ?? true\r\n });\r\n\r\n // 更新连线类型显示配置\r\n const updateLinkVisibility = () => {\r\n updateLinkConfig({\r\n linkTypeVisibility: { ...linkTypeVisibility.value }\r\n });\r\n };\r\n\r\n // 定义响应式数据\r\n const initData = ref<any[]>([]);\r\n const paneLengthPercent = ref(35);\r\n const buttonClass = ref(['button is-active', 'button', 'button', 'button']);\r\n const mode = ref('月');\r\n const startDate = ref(dayjs().locale(getDayjsLocale()).format('YYYY-MM-DD'));\r\n const minStartDate = ref(dayjs().locale(getDayjsLocale()).add(-5, 'y').format('YYYY-MM-DD'));\r\n const maxStartDate = ref(dayjs().locale(getDayjsLocale()).add(5, 'y').format('YYYY-MM-DD'));\r\n const showStartDatePicker = ref(false);\r\n const selectedStartDate = ref('点击选择日期');\r\n const endDate = ref(dayjs().locale(getDayjsLocale()).format('YYYY-MM-DD'));\r\n const minEndDate = ref(startDate.value);\r\n const maxEndDate = ref(dayjs(startDate.value).locale(getDayjsLocale()).add(5, 'y').format('YYYY-MM-DD'));\r\n const showEndDatePicker = ref(false);\r\n const selectedEndDate = ref('点击选择日期');\r\n const monthHeaders = ref<MonthHeaders[]>([]);\r\n const dayHeaders = ref<DayHeaders[]>([]);\r\n const weekHeaders = ref<WeekHeaders[]>([]);\r\n const hourHeaders = ref<HourHeaders[]>([]);\r\n const scale = ref(0);\r\n const timelineCellCount = ref(0);\r\n const startGanttDate = ref<string | null>(null);\r\n const endGanttDate = ref<string | null>(null);\r\n const result = ref('');\r\n \r\n // 甘特图容器引用\r\n const ganttContainer = ref<HTMLElement>();\r\n\r\n // 计算属性\r\n const subTask = computed(() => store.subTask);\r\n const editTask = computed(() => store.editTask);\r\n const removeTask = computed(() => store.removeTask);\r\n const rootTask = computed(() => store.rootTask);\r\n const allowChangeTaskDate = computed(() => store.allowChangeTaskDate);\r\n\r\n const setTimeLineHeaders = (newVal: string) => {\r\n const start = dayjs(selectedStartDate.value);\r\n const end = dayjs(selectedEndDate.value);\r\n // 开始时间格式是否合法\r\n if (!start.isValid()) {\r\n return;\r\n }\r\n // 结束时间格式是否合法\r\n if (!end.isValid()) {\r\n return;\r\n }\r\n // 检验开始时间结束时间范围的合法性\r\n const days = end.diff(start, 'day');\r\n if (days < 0) {\r\n return;\r\n }\r\n startGanttDate.value = selectedStartDate.value + ' 00:00:00';\r\n endGanttDate.value = selectedEndDate.value + ' 23:59:59';\r\n weekHeaders.value = [];\r\n dayHeaders.value = [];\r\n monthHeaders.value = [];\r\n hourHeaders.value = [];\r\n switch (newVal) {\r\n case '月': {\r\n scale.value = 80;\r\n const months: string[] = [];\r\n let current = start.startOf('month');\r\n while (current.isBefore(end) || current.isSame(end, 'month')) {\r\n months.push(current.format('YYYY-MM-DD'));\r\n current = current.add(1, 'month');\r\n }\r\n if (!months.some((item) => dayjs(item).format('YYYY-MM') === end.format('YYYY-MM'))) {\r\n months.push(end.format('YYYY-MM-DD'));\r\n }\r\n // 时间跨度只有一个月\r\n if (months.length === 1) {\r\n const days = end.diff(start, 'day') + 1;\r\n monthHeaders.value.push({\r\n title: start.locale(getDayjsLocale()).format('MMMM'),\r\n width: days * scale.value\r\n });\r\n } else {\r\n months.forEach((month, index) => {\r\n if (index === 0) {\r\n const endOfMonth = start.endOf('month').format('YYYY-MM-DD');\r\n const days = dayjs(endOfMonth).diff(start, 'day') + 1;\r\n monthHeaders.value.push({\r\n title: dayjs(month).locale(getDayjsLocale()).format('MMMM'),\r\n width: days * scale.value\r\n });\r\n } else if (index === months.length - 1) {\r\n const startOfMonth = end.startOf('month').format('YYYY-MM-DD');\r\n const days = end.diff(dayjs(startOfMonth), 'day') + 1;\r\n monthHeaders.value.push({\r\n title: dayjs(month).locale(getDayjsLocale()).format('MMMM'),\r\n width: days * scale.value\r\n });\r\n } else {\r\n const days = dayjs(month, 'YYYY-MM').daysInMonth();\r\n monthHeaders.value.push({\r\n title: dayjs(month).locale(getDayjsLocale()).format('MMMM'),\r\n width: days * scale.value\r\n });\r\n }\r\n });\r\n }\r\n\r\n let currentDate = start;\r\n while (currentDate.isBefore(end) || currentDate.isSame(end, 'day')) {\r\n // 中日韩语言需要添加“日”后缀\r\n const needsDaySuffix = ['zh-CN', 'ja-JP', 'ko-KR'].includes(locale.value);\r\n const caption = needsDaySuffix\r\n ? currentDate.format('DD') + '日'\r\n : currentDate.format('DD');\r\n const fullDate = currentDate.format('YYYY-MM-DD');\r\n const week = currentDate.locale(getDayjsLocale()).format('dddd');\r\n weekHeaders.value.push({\r\n title: week,\r\n width: scale.value,\r\n fulldate: fullDate\r\n });\r\n dayHeaders.value.push({\r\n title: caption,\r\n width: scale.value,\r\n fulldate: fullDate\r\n });\r\n currentDate = currentDate.add(1, 'day');\r\n }\r\n timelineCellCount.value = dayHeaders.value.length;\r\n break;\r\n }\r\n case '周': {\r\n scale.value = 120;\r\n let currentDate = start.startOf('isoWeek'); // 从周一开始\r\n const endWeek = end.endOf('isoWeek'); // 到周日结束\r\n \r\n // 生成月份表头\r\n const months: string[] = [];\r\n let monthCurrent = currentDate.startOf('month');\r\n while (monthCurrent.isBefore(endWeek) || monthCurrent.isSame(endWeek, 'month')) {\r\n months.push(monthCurrent.format('YYYY-MM-DD'));\r\n monthCurrent = monthCurrent.add(1, 'month');\r\n }\r\n \r\n months.forEach((month, index) => {\r\n const monthStart = dayjs(month);\r\n const monthEnd = monthStart.endOf('month');\r\n \r\n // 计算该月包含的周数\r\n let weekCount = 0;\r\n let weekCurrent = currentDate.clone();\r\n while (weekCurrent.isBefore(endWeek) || weekCurrent.isSame(endWeek, 'week')) {\r\n const weekStart = weekCurrent.startOf('isoWeek');\r\n const weekEnd = weekCurrent.endOf('isoWeek');\r\n \r\n // 检查这一周是否与当前月份有交集\r\n if (weekStart.isSame(monthStart, 'month') || weekEnd.isSame(monthStart, 'month') ||\r\n (weekStart.isBefore(monthEnd) && weekEnd.isAfter(monthStart))) {\r\n weekCount++;\r\n }\r\n weekCurrent = weekCurrent.add(1, 'week');\r\n }\r\n \r\n if (weekCount > 0) {\r\n // 根据语言选择月份格式\r\n const isAsian = ['zh-CN', 'ja-JP', 'ko-KR'].includes(locale.value);\r\n const monthTitle = isAsian\r\n ? monthStart.format('YYYY年MM月')\r\n : monthStart.locale(getDayjsLocale()).format('MMMM YYYY');\r\n monthHeaders.value.push({\r\n title: monthTitle,\r\n width: weekCount * scale.value\r\n });\r\n }\r\n });\r\n \r\n // 生成周表头(不生成日表头,周表头显示开始和结束日期)\r\n while (currentDate.isBefore(endWeek) || currentDate.isSame(endWeek, 'week')) {\r\n const weekStart = currentDate.startOf('isoWeek');\r\n const weekEnd = currentDate.endOf('isoWeek');\r\n \r\n // 周表头 - 显示周数和日期范围\r\n const isAsian = ['zh-CN', 'ja-JP', 'ko-KR'].includes(locale.value);\r\n const weekTitle = isAsian\r\n ? `第${currentDate.isoWeek()}周 (${weekStart.format('MM/DD')}-${weekEnd.format('MM/DD')})`\r\n : `Week ${currentDate.isoWeek()} (${weekStart.format('MM/DD')}-${weekEnd.format('MM/DD')})`;\r\n weekHeaders.value.push({\r\n title: weekTitle,\r\n width: scale.value,\r\n fulldate: weekStart.format('YYYY-MM-DD')\r\n });\r\n \r\n currentDate = currentDate.add(1, 'week');\r\n }\r\n \r\n timelineCellCount.value = weekHeaders.value.length;\r\n break;\r\n }\r\n case '日': {\r\n scale.value = 80;\r\n let currentDate = start;\r\n while (currentDate.isBefore(end) || currentDate.isSame(end, 'day')) {\r\n // 中日韩语言需要添加“日”后缀\r\n const needsDaySuffix = ['zh-CN', 'ja-JP', 'ko-KR'].includes(locale.value);\r\n const caption = needsDaySuffix\r\n ? currentDate.locale(getDayjsLocale()).format('MMMM DD') + '日'\r\n : currentDate.locale(getDayjsLocale()).format('MMMM DD');\r\n const fullDate = currentDate.format('YYYY-MM-DD');\r\n const week = currentDate.locale(getDayjsLocale()).format('dddd');\r\n weekHeaders.value.push({\r\n title: week,\r\n width: scale.value,\r\n fulldate: fullDate\r\n });\r\n dayHeaders.value.push({\r\n title: caption,\r\n width: scale.value,\r\n fulldate: fullDate\r\n });\r\n currentDate = currentDate.add(1, 'day');\r\n }\r\n timelineCellCount.value = dayHeaders.value.length;\r\n break;\r\n }\r\n case '时': {\r\n scale.value = 30;\r\n let currentDate = start;\r\n // 预先计算结束日期\r\n const endOfEndDay = end.endOf('day');\r\n while (currentDate.isBefore(endOfEndDay)) {\r\n // 中日韩语言需要添加“日”后缀\r\n const needsDaySuffix = ['zh-CN', 'ja-JP', 'ko-KR'].includes(locale.value);\r\n const caption = needsDaySuffix\r\n ? currentDate.locale(getDayjsLocale()).format('MMMM DD') + '日'\r\n : currentDate.locale(getDayjsLocale()).format('MMMM DD');\r\n const fullDate = currentDate.format('YYYY-MM-DD');\r\n const week = currentDate.locale(getDayjsLocale()).format('dddd');\r\n weekHeaders.value.push({\r\n title: week,\r\n width: 24 * scale.value,\r\n fulldate: fullDate\r\n });\r\n dayHeaders.value.push({\r\n title: caption,\r\n width: 24 * scale.value,\r\n fulldate: fullDate\r\n });\r\n for (let i = 0; i <= 23; i++) {\r\n // 中日韩语言使用“点”,其他语言使用 :00 格式\r\n const needsHourSuffix = ['zh-CN', 'ja-JP', 'ko-KR'].includes(locale.value);\r\n hourHeaders.value.push({\r\n title: needsHourSuffix ? i + '点' : `${i}:00`,\r\n width: scale.value\r\n });\r\n }\r\n // 优化代码:更新 currentDate\r\n currentDate = currentDate.add(1, 'day');\r\n }\r\n timelineCellCount.value = hourHeaders.value.length;\r\n break;\r\n }\r\n }\r\n // 选定日期后重新查询\r\n props.eventConfig.queryTask(selectedStartDate.value, selectedEndDate.value, mode.value);\r\n };\r\n\r\n const FindAllParent = (targetData: any[], pid: any) => {\r\n let parent = targetData.filter(obj => obj[mapFields.value['id']] === pid);\r\n if (parent && parent.length > 0) {\r\n result.value = parent[0].index + '.' + result.value;\r\n FindAllParent(targetData, parent[0][mapFields.value['parentId']]);\r\n }\r\n };\r\n\r\n const RecursionData = (id: any, tasks: any[], level: number) => {\r\n let findResult = tasks.filter(obj => obj[mapFields.value['parentId']] === id);\r\n if (findResult && findResult.length > 0) {\r\n level++;\r\n for (let i = 0; i < findResult.length; i++) {\r\n findResult[i].treeLevel = level;\r\n findResult[i].index = i + 1;\r\n\r\n let parent = initData.value.filter(obj => obj[mapFields.value['id']] === findResult[i][mapFields.value['parentId']]);\r\n result.value = '';\r\n if (parent && parent.length > 0) {\r\n result.value = parent[0].index + '.' + findResult[i].index;\r\n FindAllParent(initData.value, parent[0][mapFields.value['parentId']]);\r\n findResult[i].no = result.value;\r\n } else {\r\n findResult[i].no = i + 1 + '';\r\n }\r\n initData.value.push(findResult[i]);\r\n RecursionData(findResult[i][mapFields.value['id']], tasks, level);\r\n }\r\n }\r\n };\r\n\r\n const timeMode = (_mode: string) => {\r\n // 更新按钮状态\r\n for (let i = 0; i < buttonClass.value.length; i++) {\r\n buttonClass.value[i] = 'button';\r\n }\r\n\r\n switch (_mode) {\r\n case '月': {\r\n buttonClass.value[0] = 'button is-active';\r\n break;\r\n }\r\n case '周': {\r\n buttonClass.value[1] = 'button is-active';\r\n break;\r\n }\r\n case '日': {\r\n buttonClass.value[2] = 'button is-active';\r\n break;\r\n }\r\n case '时': {\r\n buttonClass.value[3] = 'button is-active';\r\n break;\r\n }\r\n }\r\n \r\n // 先设置mode,触发响应式更新\r\n mode.value = _mode;\r\n \r\n // 等待DOM更新完成后再滚动\r\n nextTick(() => {\r\n const barContentEl = document.querySelector('.table .content') as HTMLElement;\r\n if (barContentEl) {\r\n barContentEl.scrollLeft = 0;\r\n }\r\n });\r\n };\r\n\r\n const confirmStart = (value: ConfirmDateData) => {\r\n let days = dayjs(endDate.value).diff(dayjs(value.date), 'days')\r\n if (days < 0) {\r\n selectedEndDate.value = value.date\r\n endDate.value = value.date\r\n }\r\n showStartDatePicker.value = false\r\n selectedStartDate.value = value.date\r\n startDate.value = value.date\r\n minEndDate.value = value.date\r\n };\r\n\r\n const confirmEnd = (value: ConfirmDateData) => {\r\n let days = dayjs(value.date).diff(dayjs(startDate.value), 'days')\r\n if (days < 0) {\r\n selectedStartDate.value = dayjs(value.date).format('YYYY-MM-DD');\r\n startDate.value = dayjs(value.date).format('YYYY-MM-DD');\r\n }\r\n showEndDatePicker.value = false\r\n selectedEndDate.value = value.date\r\n endDate.value = value.date\r\n maxStartDate.value = value.date\r\n };\r\n\r\n // 监听 mode 和日期的变化\r\n watch([mode, selectedStartDate, selectedEndDate], ([newMode, newStartDate, newEndDate], [oldMode, oldStartDate, oldEndDate]) => {\r\n if (newMode !== oldMode || newStartDate !== oldStartDate || newEndDate !== oldEndDate) {\r\n setTimeLineHeaders(newMode);\r\n }\r\n });\r\n \r\n // 监听语言变化,重新生成时间轴表头\r\n watch(locale, () => {\r\n setTimeLineHeaders(mode.value);\r\n });\r\n\r\n watch(rootTask, (newVal) => { \r\n props.eventConfig.addRootTask(newVal);\r\n });\r\n\r\n watch(subTask, (newVal) => {\r\n props.eventConfig.addSubTask(newVal);\r\n });\r\n\r\n watch(removeTask, (newVal) => {\r\n props.eventConfig.removeTask(newVal);\r\n });\r\n\r\n watch(editTask, (newVal) => {\r\n props.eventConfig.editTask(newVal);\r\n });\r\n\r\n watch(allowChangeTaskDate, (newVal) => {\r\n props.eventConfig.allowChangeTaskDate(newVal);\r\n });\r\n\r\n // 优化:拆分watchEffect,避免不必要的触发\r\n watch(() => store.barDate, (barDate) => {\r\n if (barDate) {\r\n const { id, startDate, endDate } = barDate;\r\n if (id && startDate && endDate) {\r\n props.eventConfig.barDate(id, startDate, endDate);\r\n }\r\n }\r\n });\r\n\r\n watch(scale, (newScale) => {\r\n mutations.setScale(newScale);\r\n });\r\n\r\n watch(() => props.dataConfig.taskHeaders, (newHeaders) => {\r\n mutations.setTaskHeaders(newHeaders);\r\n });\r\n\r\n watch([monthHeaders, dayHeaders, weekHeaders, hourHeaders], ([newMonth, newDay, newWeek, newHour]) => {\r\n mutations.setMonthHeaders(newMonth);\r\n mutations.setDayHeaders(newDay);\r\n mutations.setWeekHeaders(newWeek);\r\n mutations.setHourHeaders(newHour);\r\n });\r\n\r\n watch(mode, (newMode) => {\r\n mutations.setMode(newMode);\r\n });\r\n\r\n watch(mapFields, (newFields) => {\r\n if (newFields) {\r\n mutations.setMapFields(newFields);\r\n }\r\n });\r\n\r\n watch(() => props.dataConfig.queryStartDate, (newStartDate) => {\r\n if (newStartDate) {\r\n mutations.setStartGanttDate(dayjs(newStartDate).toDate());\r\n startGanttDate.value = newStartDate;\r\n selectedStartDate.value = newStartDate;\r\n startDate.value = newStartDate;\r\n }\r\n });\r\n\r\n watch(() => props.dataConfig.queryEndDate, (newEndDate) => {\r\n if (newEndDate) {\r\n mutations.setEndGanttDate(dayjs(newEndDate).toDate());\r\n endGanttDate.value = newEndDate;\r\n selectedEndDate.value = newEndDate;\r\n endDate.value = newEndDate;\r\n }\r\n });\r\n\r\n watch(timelineCellCount, (newCount) => {\r\n if (newCount) {\r\n mutations.setTimelineCellCount(newCount);\r\n }\r\n });\r\n\r\n onBeforeMount(() => {\r\n mutations.setMonthHeaders(monthHeaders.value)\r\n mutations.setWeekHeaders(weekHeaders.value)\r\n mutations.setDayHeaders(dayHeaders.value)\r\n mutations.setHourHeaders(hourHeaders.value)\r\n mutations.setTaskHeaders(props.dataConfig.taskHeaders)\r\n mutations.setMapFields(mapFields.value)\r\n mutations.setTimelineCellCount(timelineCellCount.value)\r\n // 初始化时如果有数据,先设置\r\n if (dataSource.value && dataSource.value.length > 0) {\r\n mutations.setTasks(dataSource.value)\r\n }\r\n });\r\n\r\n // 进度更新事件处理\r\n const handleProgressUpdate = (event: Event) => {\r\n const customEvent = event as CustomEvent;\r\n if (props.eventConfig.updateProgress) {\r\n props.eventConfig.updateProgress(customEvent.detail);\r\n }\r\n };\r\n\r\n onMounted(() => {\r\n monthHeaders.value = [];\r\n weekHeaders.value = [];\r\n dayHeaders.value = [];\r\n hourHeaders.value = [];\r\n\r\n console.log('Gantt onMounted, dataSource:', dataSource.value);\r\n let level: number = 0;\r\n RecursionData('0', dataSource.value, level);\r\n mutations.setTasks(initData.value);\r\n console.log('initData after RecursionData:', initData.value);\r\n nextTick(() => {\r\n mode.value = '月';\r\n mutations.setMode(mode.value)\r\n });\r\n \r\n // 监听进度更新事件\r\n window.addEventListener('taskProgressUpdate', handleProgressUpdate);\r\n });\r\n \r\n // 优化:监听dataSource变化,使用节流避免频繁更新\r\n let updateTimer: ReturnType<typeof setTimeout> | null = null;\r\n watch(dataSource, (newVal) => {\r\n console.log('dataSource changed:', newVal);\r\n if (newVal && newVal.length > 0) {\r\n // 使用节流,避免频繁更新\r\n if (updateTimer) {\r\n clearTimeout(updateTimer);\r\n }\r\n updateTimer = setTimeout(() => {\r\n initData.value = [];\r\n let level: number = 0;\r\n RecursionData('0', newVal, level);\r\n mutations.setTasks(initData.value);\r\n console.log('Tasks updated:', initData.value);\r\n updateTimer = null;\r\n }, 100);\r\n } else if (newVal && newVal.length === 0) {\r\n // 如果数据为空,也要更新\r\n mutations.setTasks([]);\r\n }\r\n }, { immediate: false });\r\n\r\n onBeforeUnmount(() => {\r\n // 清理进度更新事件监听\r\n window.removeEventListener('taskProgressUpdate', handleProgressUpdate);\r\n });\r\n\r\n provide(Symbols.AddRootTaskSymbol, (row: Record<string, any>) => {\r\n return props.eventConfig.addRootTask(row);\r\n });\r\n\r\n // 设置Bar的颜色,传递到子组件\r\n provide(Symbols.SetBarColorSymbol, (row: Record<string, any>) => {\r\n return props.styleConfig.setBarColor(row);\r\n });\r\n \r\n // 提供甘特图容器引用给主题选择器\r\n provide('ganttContainer', ganttContainer);\r\n\r\n // 监听依赖关系变化\r\n watch(() => props.dataConfig.dependencies, (newDependencies) => {\r\n if (newDependencies && newDependencies.length > 0) {\r\n console.log('Loading dependencies:', newDependencies);\r\n // 清空现有依赖关系\r\n linkDataManager.clear();\r\n // 添加新的依赖关系\r\n newDependencies.forEach(dep => {\r\n linkDataManager.addDependency(dep);\r\n });\r\n }\r\n }, { immediate: true });\r\n\r\n // 连线配置变化处理\r\n const onLinkConfigChange = (config: any) => {\r\n console.log('连线配置已更新:', config);\r\n // 这里可以添加额外的处理逻辑,比如通知其他组件更新\r\n };\r\n\r\n // 连线类型颜色(从配置中读取,用于图例显示)\r\n const linkTypeColors = computed(() => ({\r\n finishToStart: linkConfig.linkTypeColors?.finishToStart || '#3498db',\r\n startToStart: linkConfig.linkTypeColors?.startToStart || '#2ecc71',\r\n finishToFinish: linkConfig.linkTypeColors?.finishToFinish || '#e74c3c',\r\n startToFinish: linkConfig.linkTypeColors?.startToFinish || '#f39c12'\r\n }));\r\n\r\n return {\r\n t,\r\n subTask,\r\n editTask,\r\n removeTask,\r\n rootTask,\r\n allowChangeTaskDate,\r\n paneLengthPercent,\r\n startDate,\r\n endDate,\r\n minStartDate,\r\n maxStartDate,\r\n confirmStart,\r\n minEndDate,\r\n maxEndDate,\r\n confirmEnd,\r\n buttonClass,\r\n timeMode,\r\n onLinkConfigChange,\r\n ganttContainer,\r\n linkTypeColors,\r\n linkTypeVisibility,\r\n updateLinkVisibility\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n$toolbarHeight: 70px;\r\n\r\n.page {\r\n height: 100%;\r\n width: 100%;\r\n overflow: hidden;\r\n display: flex;\r\n flex-direction: column;\r\n\r\n .toolbar {\r\n height: $toolbarHeight;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: row;\r\n align-items: center;\r\n justify-content: flex-start;\r\n padding: calc($toolbarHeight / 2);\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border-bottom: 1px solid var(--border, #d0d0d0);\r\n transition: all var(--transition-normal, 0.25s ease);\r\n\r\n .dateInput {\r\n cursor: pointer;\r\n height: calc($toolbarHeight / 1.5);\r\n width: 450px;\r\n display: flex;\r\n flex-direction: row;\r\n align-items: center;\r\n justify-content: center;\r\n margin-right: 20px;\r\n border-radius: 5px;\r\n color: var(--primary, #0078d4);\r\n font-size: 14px;\r\n transition: color var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n .buttonGroup {\r\n height: calc($toolbarHeight / 1.5);\r\n display: flex;\r\n flex-direction: row;\r\n margin-right: 20px;\r\n background: var(--bg-metal-normal);\r\n border: 1px solid var(--border);\r\n box-shadow: var(--shadow-inset), var(--shadow-outset);\r\n border-radius: 2px;\r\n overflow: hidden;\r\n transition: all var(--transition-normal);\r\n\r\n .metro-btn {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 80px;\r\n height: 100%;\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border-right: 1px solid var(--border, #d0d0d0);\r\n \r\n &:last-child {\r\n border-right: none;\r\n }\r\n\r\n &::before {\r\n content: '';\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f0f0f0));\r\n opacity: 0;\r\n transition: opacity var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n &::after {\r\n content: '';\r\n position: absolute;\r\n top: 2px;\r\n left: 2px;\r\n right: 2px;\r\n bottom: 2px;\r\n background: var(--bg-metal-pressed, linear-gradient(145deg, #e0e0e0, #f8f8f8));\r\n opacity: 0;\r\n transition: opacity var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n .metro-content {\r\n position: relative;\r\n z-index: 3;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 4px;\r\n }\r\n\r\n .metro-icon {\r\n color: var(--text-secondary, #666666);\r\n transition: color var(--transition-fast, 0.15s ease);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n\r\n .metro-text {\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--text-secondary, #555555);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n transition: color var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n &:hover {\r\n &::before {\r\n opacity: 1;\r\n }\r\n\r\n .metro-icon {\r\n color: var(--text-primary, #333333);\r\n }\r\n\r\n .metro-text {\r\n color: var(--text-primary, #333333);\r\n }\r\n }\r\n\r\n &:active {\r\n &::after {\r\n opacity: 1;\r\n }\r\n }\r\n\r\n // 激活状态\r\n &.button.is-active,\r\n &.is-active {\r\n background: linear-gradient(145deg, #0078d4, #106ebe);\r\n box-shadow: \r\n inset 0 1px 0 rgba(255, 255, 255, 0.2),\r\n inset 0 -1px 0 rgba(0, 0, 0, 0.3),\r\n 0 1px 2px rgba(0, 0, 0, 0.2);\r\n\r\n &::before {\r\n background: linear-gradient(145deg, #1084d8, #0d5aa7);\r\n opacity: 0;\r\n }\r\n\r\n &::after {\r\n background: linear-gradient(145deg, #0d5aa7, #1084d8);\r\n opacity: 0;\r\n }\r\n\r\n .metro-icon {\r\n color: #ffffff;\r\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);\r\n }\r\n\r\n .metro-text {\r\n color: #ffffff;\r\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);\r\n font-weight: 700;\r\n }\r\n\r\n &:hover {\r\n &::before {\r\n opacity: 1;\r\n }\r\n }\r\n\r\n &:active {\r\n &::after {\r\n opacity: 1;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // 兼容旧的class名称\r\n .button {\r\n @extend .metro-btn;\r\n }\r\n }\r\n \r\n .link-legend {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n padding: 6px 16px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n border-radius: 4px;\r\n margin-right: 16px;\r\n\r\n .legend-title {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--text-secondary, #666666);\r\n padding-right: 12px;\r\n border-right: 1px solid var(--border, #d0d0d0);\r\n \r\n svg {\r\n color: var(--primary, #0078d4);\r\n }\r\n }\r\n\r\n .legend-items {\r\n display: flex;\r\n gap: 12px;\r\n }\r\n\r\n .legend-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n cursor: pointer;\r\n padding: 2px 6px;\r\n border-radius: 3px;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n\r\n input[type=\"checkbox\"] {\r\n width: 14px;\r\n height: 14px;\r\n cursor: pointer;\r\n accent-color: var(--primary, #0078d4);\r\n margin: 0;\r\n }\r\n\r\n &:hover {\r\n background: var(--bg-secondary, #e8e8e8);\r\n }\r\n\r\n &.disabled {\r\n opacity: 0.5;\r\n \r\n svg {\r\n opacity: 0.4;\r\n }\r\n \r\n .legend-label,\r\n .legend-desc {\r\n text-decoration: line-through;\r\n }\r\n }\r\n\r\n .legend-label {\r\n font-size: 10px;\r\n font-weight: 700;\r\n color: var(--text-primary, #333333);\r\n min-width: 18px;\r\n }\r\n\r\n .legend-desc {\r\n font-size: 10px;\r\n color: var(--text-secondary, #666666);\r\n white-space: nowrap;\r\n }\r\n }\r\n }\r\n\r\n .config-buttons {\r\n display: flex;\r\n gap: 12px;\r\n margin-left: auto;\r\n padding: 0 8px;\r\n }\r\n \r\n .link-config-btn {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n padding: 10px 16px;\r\n background: linear-gradient(135deg, \r\n rgba(52, 152, 219, 0.1) 0%, \r\n rgba(155, 89, 182, 0.1) 50%, \r\n rgba(52, 152, 219, 0.1) 100%);\r\n border: 1px solid rgba(52, 152, 219, 0.3);\r\n border-radius: 8px;\r\n cursor: pointer;\r\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n overflow: hidden;\r\n \r\n &::before {\r\n content: '';\r\n position: absolute;\r\n top: 0;\r\n left: -100%;\r\n width: 100%;\r\n height: 100%;\r\n background: linear-gradient(90deg, \r\n transparent, \r\n rgba(255, 255, 255, 0.2), \r\n transparent);\r\n transition: left 0.5s ease;\r\n }\r\n \r\n .btn-content {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n z-index: 2;\r\n }\r\n \r\n .btn-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n \r\n svg {\r\n width: 18px;\r\n height: 18px;\r\n color: #3498db;\r\n transition: all 0.3s ease;\r\n filter: drop-shadow(0 0 3px rgba(52, 152, 219, 0.3));\r\n }\r\n }\r\n \r\n .btn-text {\r\n font-size: 13px;\r\n font-weight: 600;\r\n color: #2c3e50;\r\n transition: all 0.3s ease;\r\n }\r\n \r\n // 悬停效果\r\n &:hover {\r\n transform: translateY(-2px);\r\n background: linear-gradient(135deg, \r\n rgba(52, 152, 219, 0.2) 0%, \r\n rgba(155, 89, 182, 0.2) 50%, \r\n rgba(52, 152, 219, 0.2) 100%);\r\n border-color: rgba(52, 152, 219, 0.5);\r\n box-shadow: 0 4px 12px rgba(52, 152, 219, 0.2);\r\n \r\n &::before {\r\n left: 100%;\r\n }\r\n \r\n .btn-icon svg {\r\n color: #2980b9;\r\n transform: scale(1.1);\r\n }\r\n \r\n .btn-text {\r\n color: #2980b9;\r\n }\r\n }\r\n \r\n // 激活状态\r\n &.active {\r\n background: linear-gradient(135deg, \r\n rgba(231, 76, 60, 0.15) 0%, \r\n rgba(192, 57, 43, 0.15) 50%, \r\n rgba(231, 76, 60, 0.15) 100%);\r\n border-color: rgba(231, 76, 60, 0.4);\r\n box-shadow: 0 0 15px rgba(231, 76, 60, 0.3);\r\n \r\n .btn-icon svg {\r\n color: #e74c3c;\r\n animation: pulse 2s infinite ease-in-out;\r\n }\r\n \r\n .btn-text {\r\n color: #e74c3c;\r\n font-weight: 700;\r\n }\r\n }\r\n }\r\n \r\n // 动画定义\r\n @keyframes pulse {\r\n 0%, 100% {\r\n transform: scale(1);\r\n filter: drop-shadow(0 0 3px rgba(231, 76, 60, 0.3));\r\n }\r\n 50% {\r\n transform: scale(1.1);\r\n filter: drop-shadow(0 0 8px rgba(231, 76, 60, 0.6));\r\n }\r\n }\r\n }\r\n\r\n .gantt {\r\n height: calc(100% - #{$toolbarHeight});\r\n width: 100%;\r\n }\r\n \r\n .config-panel-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 1000;\r\n }\r\n \r\n .config-panel-container {\r\n position: relative;\r\n max-height: 90vh;\r\n overflow: hidden;\r\n }\r\n}\r\n</style>","<template>\r\n <div class=\"page gantt-container\" ref=\"ganttContainer\">\r\n <div class=\"toolbar\">\r\n <div class=\"dateInput\">\r\n <DatePicker :date=\"startDate\" :min-date=\"minStartDate\" :max-date=\"maxStartDate\"\r\n @confirm=\"confirmStart\" />\r\n <span style=\"margin-right:20px;margin-left:20px;color:#606266\">{{ t('common.to') }}</span>\r\n <DatePicker :date=\"endDate\" :min-date=\"minEndDate\" :max-date=\"maxEndDate\" @confirm=\"confirmEnd\" />\r\n </div>\r\n <div class=\"buttonGroup\">\r\n <div :class=\"buttonClass[0]\" class=\"metro-btn\" @click=\"timeMode('月')\">\r\n <div class=\"metro-content\">\r\n <div class=\"metro-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z\"/>\r\n </svg>\r\n </div>\r\n <div class=\"metro-text\">{{ t('viewMode.month') }}</div>\r\n </div>\r\n </div>\r\n <div :class=\"buttonClass[1]\" class=\"metro-btn\" @click=\"timeMode('周')\">\r\n <div class=\"metro-content\">\r\n <div class=\"metro-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z\"/>\r\n </svg>\r\n </div>\r\n <div class=\"metro-text\">{{ t('viewMode.week') }}</div>\r\n </div>\r\n </div>\r\n <div :class=\"buttonClass[2]\" class=\"metro-btn\" @click=\"timeMode('日')\">\r\n <div class=\"metro-content\">\r\n <div class=\"metro-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 2 2h8c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 18H6V4h8v16z\"/>\r\n </svg>\r\n </div>\r\n <div class=\"metro-text\">{{ t('viewMode.day') }}</div>\r\n </div>\r\n </div>\r\n <div :class=\"buttonClass[3]\" class=\"metro-btn\" @click=\"timeMode('时')\">\r\n <div class=\"metro-content\">\r\n <div class=\"metro-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z\"/>\r\n </svg>\r\n </div>\r\n <div class=\"metro-text\">{{ t('viewMode.hour') }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n <!-- 连线类型图例 -->\r\n <div class=\"link-legend\">\r\n <div class=\"legend-title\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M19 15l-6 6-1.42-1.42L15.17 16H4V4h2v10h9.17l-3.59-3.58L13 9l6 6z\"/>\r\n </svg>\r\n {{ t('link.legend') }}\r\n </div>\r\n <div class=\"legend-items\">\r\n <label class=\"legend-item\" :class=\"{ disabled: !linkTypeVisibility.parentChild }\" title=\"父子关系:显示任务的层级结构\">\r\n <input type=\"checkbox\" v-model=\"linkTypeVisibility.parentChild\" @change=\"updateLinkVisibility\" />\r\n <svg width=\"24\" height=\"12\" viewBox=\"0 0 24 12\">\r\n <line x1=\"0\" y1=\"6\" x2=\"18\" y2=\"6\" stroke=\"#95a5a6\" stroke-width=\"1.5\" stroke-dasharray=\"3,3\"/>\r\n <polygon points=\"24,6 18,3 18,9\" fill=\"#95a5a6\"/>\r\n </svg>\r\n <span class=\"legend-label\">{{ t('link.pc') }}</span>\r\n <span class=\"legend-desc\">{{ t('link.parentChild') }}</span>\r\n </label>\r\n <label class=\"legend-item\" :class=\"{ disabled: !linkTypeVisibility.finishToStart }\" title=\"完成-开始:前置任务完成后,后续任务才能开始\">\r\n <input type=\"checkbox\" v-model=\"linkTypeVisibility.finishToStart\" @change=\"updateLinkVisibility\" />\r\n <svg width=\"24\" height=\"12\" viewBox=\"0 0 24 12\">\r\n <line x1=\"0\" y1=\"6\" x2=\"18\" y2=\"6\" :stroke=\"linkTypeColors.finishToStart\" stroke-width=\"2\"/>\r\n <polygon points=\"24,6 18,3 18,9\" :fill=\"linkTypeColors.finishToStart\"/>\r\n </svg>\r\n <span class=\"legend-label\">{{ t('link.fs') }}</span>\r\n <span class=\"legend-desc\">{{ t('link.finishToStart') }}</span>\r\n </label>\r\n <label class=\"legend-item\" :class=\"{ disabled: !linkTypeVisibility.startToStart }\" title=\"开始-开始:两个任务同时开始\">\r\n <input type=\"checkbox\" v-model=\"linkTypeVisibility.startToStart\" @change=\"updateLinkVisibility\" />\r\n <svg width=\"24\" height=\"12\" viewBox=\"0 0 24 12\">\r\n <line x1=\"0\" y1=\"6\" x2=\"18\" y2=\"6\" :stroke=\"linkTypeColors.startToStart\" stroke-width=\"2\"/>\r\n <polygon points=\"24,6 18,3 18,9\" :fill=\"linkTypeColors.startToStart\"/>\r\n </svg>\r\n <span class=\"legend-label\">{{ t('link.ss') }}</span>\r\n <span class=\"legend-desc\">{{ t('link.startToStart') }}</span>\r\n </label>\r\n <label class=\"legend-item\" :class=\"{ disabled: !linkTypeVisibility.finishToFinish }\" title=\"完成-完成:两个任务同时完成\">\r\n <input type=\"checkbox\" v-model=\"linkTypeVisibility.finishToFinish\" @change=\"updateLinkVisibility\" />\r\n <svg width=\"24\" height=\"12\" viewBox=\"0 0 24 12\">\r\n <line x1=\"0\" y1=\"6\" x2=\"18\" y2=\"6\" :stroke=\"linkTypeColors.finishToFinish\" stroke-width=\"2\"/>\r\n <polygon points=\"24,6 18,3 18,9\" :fill=\"linkTypeColors.finishToFinish\"/>\r\n </svg>\r\n <span class=\"legend-label\">{{ t('link.ff') }}</span>\r\n <span class=\"legend-desc\">{{ t('link.finishToFinish') }}</span>\r\n </label>\r\n <label class=\"legend-item\" :class=\"{ disabled: !linkTypeVisibility.startToFinish }\" title=\"开始-完成:前置任务开始后,后续任务才能完成\">\r\n <input type=\"checkbox\" v-model=\"linkTypeVisibility.startToFinish\" @change=\"updateLinkVisibility\" />\r\n <svg width=\"24\" height=\"12\" viewBox=\"0 0 24 12\">\r\n <line x1=\"0\" y1=\"6\" x2=\"18\" y2=\"6\" :stroke=\"linkTypeColors.startToFinish\" stroke-width=\"2\"/>\r\n <polygon points=\"24,6 18,3 18,9\" :fill=\"linkTypeColors.startToFinish\"/>\r\n </svg>\r\n <span class=\"legend-label\">{{ t('link.sf') }}</span>\r\n <span class=\"legend-desc\">{{ t('link.startToFinish') }}</span>\r\n </label>\r\n </div>\r\n </div>\r\n <div class=\"config-buttons\">\r\n <GanttConfigPanel />\r\n </div>\r\n </div>\r\n <div class=\"gantt\">\r\n <SplitPane direction=\"row\" :min=\"0\" :max=\"100\" :triggerLength=\"10\"\r\n v-model:paneLengthPercent=\"paneLengthPercent\">\r\n <template #one>\r\n <TaskTable :headersHeight='styleConfig.headersHeight' :rowHeight='styleConfig.rowHeight'>\r\n </TaskTable>\r\n </template>\r\n <template #two>\r\n <RightTable ref='barContent' :headersHeight='styleConfig.headersHeight'\r\n :rowHeight='styleConfig.rowHeight'></RightTable>\r\n </template>\r\n </SplitPane>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { ref, defineComponent, computed, provide, onBeforeMount, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';\r\nimport dayjs from 'dayjs';\r\nimport customParseFormat from 'dayjs/plugin/customParseFormat';\r\nimport 'dayjs/locale/zh-cn';\r\nimport 'dayjs/locale/en';\r\nimport 'dayjs/locale/ja';\r\nimport 'dayjs/locale/ko';\r\nimport 'dayjs/locale/fr';\r\nimport 'dayjs/locale/de';\r\nimport 'dayjs/locale/es';\r\nimport 'dayjs/locale/ru';\r\ndayjs.extend(customParseFormat);\r\nimport { Symbols } from './Symbols';\r\nimport { linkDataManager, useLinkConfig } from './LinkConfig';\r\nimport { useI18n } from './i18n';\r\n// 导入日期选择器组件\r\nimport DatePicker from './DatePicker.vue';\r\n// 导入分割面板组件\r\nimport SplitPane from './SplitPane.vue';\r\nimport TaskTable from '../gantt/task/TaskTable.vue';\r\nimport RightTable from './RightTable.vue';\r\nimport GanttConfigPanel from './GanttConfigPanel.vue';\r\nimport { store, mutations } from './Store';\r\nexport type { DataConfig, StyleConfig, EventConfig, TaskHeader } from './Types';\r\n// 移除未使用的类型导入\r\nimport { type ConfirmDateData } from './ZodSchema';\r\n\r\n// 定义月份表头类型\r\ntype MonthHeaders = {\r\n title: string;\r\n width: number;\r\n}\r\n\r\n// 定义天表头类型\r\ntype DayHeaders = MonthHeaders & {\r\n fulldate: string;\r\n}\r\n\r\n// 定义周表头类型\r\ntype WeekHeaders = MonthHeaders & {\r\n fulldate: string;\r\n}\r\n\r\n// 定义小时表头类型\r\ntype HourHeaders = {\r\n title: string;\r\n width: number;\r\n}\r\n\r\n/**\r\n * Gantt 组件,用于展示甘特图。\r\n * 该组件接收样式配置、数据配置和事件配置作为属性。\r\n */\r\nexport default defineComponent({\r\n // 组件名称\r\n name: 'Gantt',\r\n props: {\r\n /**\r\n * 样式配置对象,包含表头高度和行高。\r\n * @type {{ headersHeight: number; rowHeight: number }}\r\n * @required\r\n * @default { headersHeight: 100, rowHeight: 60 }\r\n */\r\n styleConfig: {\r\n type: Object as () => {\r\n headersHeight: number;\r\n rowHeight: number;\r\n setBarColor: (row: Record<string, any>) => string;\r\n },\r\n required: true,\r\n default: () => ({\r\n // 表头高度\r\n headersHeight: 100,\r\n // 行高\r\n rowHeight: 60\r\n }),\r\n // 验证参数合法性\r\n validator: (value: { headersHeight: number; rowHeight: number }) => {\r\n return value.headersHeight > 0 && value.rowHeight > 0;\r\n }\r\n },\r\n /**\r\n * 数据配置对象,包含数据源、任务表头和字段映射函数。\r\n * @type {{ dataSource: any[]; taskHeaders: () => any[]; mapFields: () => any }}\r\n * @required\r\n * @default { dataSource: [], taskHeaders: () => [], mapFields: () => {} }\r\n */\r\n dataConfig: {\r\n type: Object as () => {\r\n dataSource: any[];\r\n taskHeaders: any[];\r\n mapFields: Record<string, any>;\r\n queryStartDate: string;\r\n queryEndDate: string;\r\n dependencies?: Omit<import('./Types').TaskDependency, 'id'>[];\r\n },\r\n required: true,\r\n default: () => ({\r\n dataSource: [],\r\n taskHeaders: [],\r\n mapFields: {}\r\n }),\r\n validator: (value: { dataSource: any[]; taskHeaders: any[]; mapFields: Record<string, any> }) => {\r\n return Array.isArray(value.dataSource) &&\r\n Array.isArray(value.taskHeaders) &&\r\n typeof value.mapFields === 'object' && value.mapFields !== null;\r\n }\r\n },\r\n /**\r\n * 事件配置对象,包含添加根任务、添加子任务、移除任务等事件处理函数。\r\n * @type {{\r\n * addRootTask: () => void;\r\n * addSubTask: (task: any) => void;\r\n * removeTask: (task: any) => void;\r\n * queryTask: (startDate: string, endDate: string, mode: string) => void;\r\n * barDate: (id: any, startDate: string, endDate: string) => void;\r\n * allowChangeTaskDate: (allow: boolean) => void;\r\n * }}\r\n * @required\r\n */\r\n eventConfig: {\r\n type: Object as () => {\r\n addRootTask: (row: Record<string, any> | null) => void;\r\n addSubTask: (task: any) => void;\r\n removeTask: (task: any) => void;\r\n editTask: (task: any) => void;\r\n queryTask: (startDate: string, endDate: string, mode: string) => void;\r\n barDate: (id: any, startDate: string, endDate: string) => void;\r\n allowChangeTaskDate: (allow: boolean) => void;\r\n updateProgress?: (detail: { taskId: any; oldProgress: number; newProgress: number; task: Record<string, any> }) => void;\r\n },\r\n required: true\r\n }\r\n },\r\n components: {\r\n DatePicker,\r\n SplitPane,\r\n TaskTable,\r\n RightTable,\r\n GanttConfigPanel\r\n },\r\n setup(props) {\r\n // 国际化\r\n const { t, locale } = useI18n();\r\n \r\n // 根据当前语言设置 dayjs locale\r\n const getDayjsLocale = () => {\r\n const localeMap: Record<string, string> = {\r\n 'zh-CN': 'zh-cn',\r\n 'en-US': 'en',\r\n 'ja-JP': 'ja',\r\n 'ko-KR': 'ko',\r\n 'fr-FR': 'fr',\r\n 'de-DE': 'de',\r\n 'es-ES': 'es',\r\n 'ru-RU': 'ru'\r\n };\r\n return localeMap[locale.value] || 'en';\r\n };\r\n \r\n // 缓存 mapFields 的结果\r\n const mapFields = computed(() => props.dataConfig.mapFields);\r\n // 缓存 dataSource 的结果\r\n const dataSource = computed(() => props.dataConfig.dataSource);\r\n\r\n // 连线配置\r\n const { config: linkConfig, updateConfig: updateLinkConfig } = useLinkConfig();\r\n\r\n // 连线类型显示控制\r\n const linkTypeVisibility = ref({\r\n finishToStart: linkConfig.linkTypeVisibility?.finishToStart ?? true,\r\n startToStart: linkConfig.linkTypeVisibility?.startToStart ?? true,\r\n finishToFinish: linkConfig.linkTypeVisibility?.finishToFinish ?? true,\r\n startToFinish: linkConfig.linkTypeVisibility?.startToFinish ?? true,\r\n parentChild: linkConfig.linkTypeVisibility?.parentChild ?? true\r\n });\r\n\r\n // 更新连线类型显示配置\r\n const updateLinkVisibility = () => {\r\n updateLinkConfig({\r\n linkTypeVisibility: { ...linkTypeVisibility.value }\r\n });\r\n };\r\n\r\n // 定义响应式数据\r\n const initData = ref<any[]>([]);\r\n const paneLengthPercent = ref(35);\r\n const buttonClass = ref(['button is-active', 'button', 'button', 'button']);\r\n const mode = ref('月');\r\n const startDate = ref(dayjs().locale(getDayjsLocale()).format('YYYY-MM-DD'));\r\n const minStartDate = ref(dayjs().locale(getDayjsLocale()).add(-5, 'y').format('YYYY-MM-DD'));\r\n const maxStartDate = ref(dayjs().locale(getDayjsLocale()).add(5, 'y').format('YYYY-MM-DD'));\r\n const showStartDatePicker = ref(false);\r\n const selectedStartDate = ref('点击选择日期');\r\n const endDate = ref(dayjs().locale(getDayjsLocale()).format('YYYY-MM-DD'));\r\n const minEndDate = ref(startDate.value);\r\n const maxEndDate = ref(dayjs(startDate.value).locale(getDayjsLocale()).add(5, 'y').format('YYYY-MM-DD'));\r\n const showEndDatePicker = ref(false);\r\n const selectedEndDate = ref('点击选择日期');\r\n const monthHeaders = ref<MonthHeaders[]>([]);\r\n const dayHeaders = ref<DayHeaders[]>([]);\r\n const weekHeaders = ref<WeekHeaders[]>([]);\r\n const hourHeaders = ref<HourHeaders[]>([]);\r\n const scale = ref(0);\r\n const timelineCellCount = ref(0);\r\n const startGanttDate = ref<string | null>(null);\r\n const endGanttDate = ref<string | null>(null);\r\n const result = ref('');\r\n \r\n // 甘特图容器引用\r\n const ganttContainer = ref<HTMLElement>();\r\n\r\n // 计算属性\r\n const subTask = computed(() => store.subTask);\r\n const editTask = computed(() => store.editTask);\r\n const removeTask = computed(() => store.removeTask);\r\n const rootTask = computed(() => store.rootTask);\r\n const allowChangeTaskDate = computed(() => store.allowChangeTaskDate);\r\n\r\n const setTimeLineHeaders = (newVal: string) => {\r\n const start = dayjs(selectedStartDate.value);\r\n const end = dayjs(selectedEndDate.value);\r\n // 开始时间格式是否合法\r\n if (!start.isValid()) {\r\n return;\r\n }\r\n // 结束时间格式是否合法\r\n if (!end.isValid()) {\r\n return;\r\n }\r\n // 检验开始时间结束时间范围的合法性\r\n const days = end.diff(start, 'day');\r\n if (days < 0) {\r\n return;\r\n }\r\n startGanttDate.value = selectedStartDate.value + ' 00:00:00';\r\n endGanttDate.value = selectedEndDate.value + ' 23:59:59';\r\n weekHeaders.value = [];\r\n dayHeaders.value = [];\r\n monthHeaders.value = [];\r\n hourHeaders.value = [];\r\n switch (newVal) {\r\n case '月': {\r\n scale.value = 80;\r\n const months: string[] = [];\r\n let current = start.startOf('month');\r\n while (current.isBefore(end) || current.isSame(end, 'month')) {\r\n months.push(current.format('YYYY-MM-DD'));\r\n current = current.add(1, 'month');\r\n }\r\n if (!months.some((item) => dayjs(item).format('YYYY-MM') === end.format('YYYY-MM'))) {\r\n months.push(end.format('YYYY-MM-DD'));\r\n }\r\n // 时间跨度只有一个月\r\n if (months.length === 1) {\r\n const days = end.diff(start, 'day') + 1;\r\n monthHeaders.value.push({\r\n title: start.locale(getDayjsLocale()).format('MMMM'),\r\n width: days * scale.value\r\n });\r\n } else {\r\n months.forEach((month, index) => {\r\n if (index === 0) {\r\n const endOfMonth = start.endOf('month').format('YYYY-MM-DD');\r\n const days = dayjs(endOfMonth).diff(start, 'day') + 1;\r\n monthHeaders.value.push({\r\n title: dayjs(month).locale(getDayjsLocale()).format('MMMM'),\r\n width: days * scale.value\r\n });\r\n } else if (index === months.length - 1) {\r\n const startOfMonth = end.startOf('month').format('YYYY-MM-DD');\r\n const days = end.diff(dayjs(startOfMonth), 'day') + 1;\r\n monthHeaders.value.push({\r\n title: dayjs(month).locale(getDayjsLocale()).format('MMMM'),\r\n width: days * scale.value\r\n });\r\n } else {\r\n const days = dayjs(month, 'YYYY-MM').daysInMonth();\r\n monthHeaders.value.push({\r\n title: dayjs(month).locale(getDayjsLocale()).format('MMMM'),\r\n width: days * scale.value\r\n });\r\n }\r\n });\r\n }\r\n\r\n let currentDate = start;\r\n while (currentDate.isBefore(end) || currentDate.isSame(end, 'day')) {\r\n // 中日韩语言需要添加“日”后缀\r\n const needsDaySuffix = ['zh-CN', 'ja-JP', 'ko-KR'].includes(locale.value);\r\n const caption = needsDaySuffix\r\n ? currentDate.format('DD') + '日'\r\n : currentDate.format('DD');\r\n const fullDate = currentDate.format('YYYY-MM-DD');\r\n const week = currentDate.locale(getDayjsLocale()).format('dddd');\r\n weekHeaders.value.push({\r\n title: week,\r\n width: scale.value,\r\n fulldate: fullDate\r\n });\r\n dayHeaders.value.push({\r\n title: caption,\r\n width: scale.value,\r\n fulldate: fullDate\r\n });\r\n currentDate = currentDate.add(1, 'day');\r\n }\r\n timelineCellCount.value = dayHeaders.value.length;\r\n break;\r\n }\r\n case '周': {\r\n scale.value = 120;\r\n let currentDate = start.startOf('isoWeek'); // 从周一开始\r\n const endWeek = end.endOf('isoWeek'); // 到周日结束\r\n \r\n // 生成月份表头\r\n const months: string[] = [];\r\n let monthCurrent = currentDate.startOf('month');\r\n while (monthCurrent.isBefore(endWeek) || monthCurrent.isSame(endWeek, 'month')) {\r\n months.push(monthCurrent.format('YYYY-MM-DD'));\r\n monthCurrent = monthCurrent.add(1, 'month');\r\n }\r\n \r\n months.forEach((month, index) => {\r\n const monthStart = dayjs(month);\r\n const monthEnd = monthStart.endOf('month');\r\n \r\n // 计算该月包含的周数\r\n let weekCount = 0;\r\n let weekCurrent = currentDate.clone();\r\n while (weekCurrent.isBefore(endWeek) || weekCurrent.isSame(endWeek, 'week')) {\r\n const weekStart = weekCurrent.startOf('isoWeek');\r\n const weekEnd = weekCurrent.endOf('isoWeek');\r\n \r\n // 检查这一周是否与当前月份有交集\r\n if (weekStart.isSame(monthStart, 'month') || weekEnd.isSame(monthStart, 'month') ||\r\n (weekStart.isBefore(monthEnd) && weekEnd.isAfter(monthStart))) {\r\n weekCount++;\r\n }\r\n weekCurrent = weekCurrent.add(1, 'week');\r\n }\r\n \r\n if (weekCount > 0) {\r\n // 根据语言选择月份格式\r\n const isAsian = ['zh-CN', 'ja-JP', 'ko-KR'].includes(locale.value);\r\n const monthTitle = isAsian\r\n ? monthStart.format('YYYY年MM月')\r\n : monthStart.locale(getDayjsLocale()).format('MMMM YYYY');\r\n monthHeaders.value.push({\r\n title: monthTitle,\r\n width: weekCount * scale.value\r\n });\r\n }\r\n });\r\n \r\n // 生成周表头(不生成日表头,周表头显示开始和结束日期)\r\n while (currentDate.isBefore(endWeek) || currentDate.isSame(endWeek, 'week')) {\r\n const weekStart = currentDate.startOf('isoWeek');\r\n const weekEnd = currentDate.endOf('isoWeek');\r\n \r\n // 周表头 - 显示周数和日期范围\r\n const isAsian = ['zh-CN', 'ja-JP', 'ko-KR'].includes(locale.value);\r\n const weekTitle = isAsian\r\n ? `第${currentDate.isoWeek()}周 (${weekStart.format('MM/DD')}-${weekEnd.format('MM/DD')})`\r\n : `Week ${currentDate.isoWeek()} (${weekStart.format('MM/DD')}-${weekEnd.format('MM/DD')})`;\r\n weekHeaders.value.push({\r\n title: weekTitle,\r\n width: scale.value,\r\n fulldate: weekStart.format('YYYY-MM-DD')\r\n });\r\n \r\n currentDate = currentDate.add(1, 'week');\r\n }\r\n \r\n timelineCellCount.value = weekHeaders.value.length;\r\n break;\r\n }\r\n case '日': {\r\n scale.value = 80;\r\n let currentDate = start;\r\n while (currentDate.isBefore(end) || currentDate.isSame(end, 'day')) {\r\n // 中日韩语言需要添加“日”后缀\r\n const needsDaySuffix = ['zh-CN', 'ja-JP', 'ko-KR'].includes(locale.value);\r\n const caption = needsDaySuffix\r\n ? currentDate.locale(getDayjsLocale()).format('MMMM DD') + '日'\r\n : currentDate.locale(getDayjsLocale()).format('MMMM DD');\r\n const fullDate = currentDate.format('YYYY-MM-DD');\r\n const week = currentDate.locale(getDayjsLocale()).format('dddd');\r\n weekHeaders.value.push({\r\n title: week,\r\n width: scale.value,\r\n fulldate: fullDate\r\n });\r\n dayHeaders.value.push({\r\n title: caption,\r\n width: scale.value,\r\n fulldate: fullDate\r\n });\r\n currentDate = currentDate.add(1, 'day');\r\n }\r\n timelineCellCount.value = dayHeaders.value.length;\r\n break;\r\n }\r\n case '时': {\r\n scale.value = 30;\r\n let currentDate = start;\r\n // 预先计算结束日期\r\n const endOfEndDay = end.endOf('day');\r\n while (currentDate.isBefore(endOfEndDay)) {\r\n // 中日韩语言需要添加“日”后缀\r\n const needsDaySuffix = ['zh-CN', 'ja-JP', 'ko-KR'].includes(locale.value);\r\n const caption = needsDaySuffix\r\n ? currentDate.locale(getDayjsLocale()).format('MMMM DD') + '日'\r\n : currentDate.locale(getDayjsLocale()).format('MMMM DD');\r\n const fullDate = currentDate.format('YYYY-MM-DD');\r\n const week = currentDate.locale(getDayjsLocale()).format('dddd');\r\n weekHeaders.value.push({\r\n title: week,\r\n width: 24 * scale.value,\r\n fulldate: fullDate\r\n });\r\n dayHeaders.value.push({\r\n title: caption,\r\n width: 24 * scale.value,\r\n fulldate: fullDate\r\n });\r\n for (let i = 0; i <= 23; i++) {\r\n // 中日韩语言使用“点”,其他语言使用 :00 格式\r\n const needsHourSuffix = ['zh-CN', 'ja-JP', 'ko-KR'].includes(locale.value);\r\n hourHeaders.value.push({\r\n title: needsHourSuffix ? i + '点' : `${i}:00`,\r\n width: scale.value\r\n });\r\n }\r\n // 优化代码:更新 currentDate\r\n currentDate = currentDate.add(1, 'day');\r\n }\r\n timelineCellCount.value = hourHeaders.value.length;\r\n break;\r\n }\r\n }\r\n // 选定日期后重新查询\r\n props.eventConfig.queryTask(selectedStartDate.value, selectedEndDate.value, mode.value);\r\n };\r\n\r\n const FindAllParent = (targetData: any[], pid: any) => {\r\n let parent = targetData.filter(obj => obj[mapFields.value['id']] === pid);\r\n if (parent && parent.length > 0) {\r\n result.value = parent[0].index + '.' + result.value;\r\n FindAllParent(targetData, parent[0][mapFields.value['parentId']]);\r\n }\r\n };\r\n\r\n const RecursionData = (id: any, tasks: any[], level: number) => {\r\n let findResult = tasks.filter(obj => obj[mapFields.value['parentId']] === id);\r\n if (findResult && findResult.length > 0) {\r\n level++;\r\n for (let i = 0; i < findResult.length; i++) {\r\n findResult[i].treeLevel = level;\r\n findResult[i].index = i + 1;\r\n\r\n let parent = initData.value.filter(obj => obj[mapFields.value['id']] === findResult[i][mapFields.value['parentId']]);\r\n result.value = '';\r\n if (parent && parent.length > 0) {\r\n result.value = parent[0].index + '.' + findResult[i].index;\r\n FindAllParent(initData.value, parent[0][mapFields.value['parentId']]);\r\n findResult[i].no = result.value;\r\n } else {\r\n findResult[i].no = i + 1 + '';\r\n }\r\n initData.value.push(findResult[i]);\r\n RecursionData(findResult[i][mapFields.value['id']], tasks, level);\r\n }\r\n }\r\n };\r\n\r\n const timeMode = (_mode: string) => {\r\n // 更新按钮状态\r\n for (let i = 0; i < buttonClass.value.length; i++) {\r\n buttonClass.value[i] = 'button';\r\n }\r\n\r\n switch (_mode) {\r\n case '月': {\r\n buttonClass.value[0] = 'button is-active';\r\n break;\r\n }\r\n case '周': {\r\n buttonClass.value[1] = 'button is-active';\r\n break;\r\n }\r\n case '日': {\r\n buttonClass.value[2] = 'button is-active';\r\n break;\r\n }\r\n case '时': {\r\n buttonClass.value[3] = 'button is-active';\r\n break;\r\n }\r\n }\r\n \r\n // 先设置mode,触发响应式更新\r\n mode.value = _mode;\r\n \r\n // 等待DOM更新完成后再滚动\r\n nextTick(() => {\r\n const barContentEl = document.querySelector('.table .content') as HTMLElement;\r\n if (barContentEl) {\r\n barContentEl.scrollLeft = 0;\r\n }\r\n });\r\n };\r\n\r\n const confirmStart = (value: ConfirmDateData) => {\r\n let days = dayjs(endDate.value).diff(dayjs(value.date), 'days')\r\n if (days < 0) {\r\n selectedEndDate.value = value.date\r\n endDate.value = value.date\r\n }\r\n showStartDatePicker.value = false\r\n selectedStartDate.value = value.date\r\n startDate.value = value.date\r\n minEndDate.value = value.date\r\n };\r\n\r\n const confirmEnd = (value: ConfirmDateData) => {\r\n let days = dayjs(value.date).diff(dayjs(startDate.value), 'days')\r\n if (days < 0) {\r\n selectedStartDate.value = dayjs(value.date).format('YYYY-MM-DD');\r\n startDate.value = dayjs(value.date).format('YYYY-MM-DD');\r\n }\r\n showEndDatePicker.value = false\r\n selectedEndDate.value = value.date\r\n endDate.value = value.date\r\n maxStartDate.value = value.date\r\n };\r\n\r\n // 监听 mode 和日期的变化\r\n watch([mode, selectedStartDate, selectedEndDate], ([newMode, newStartDate, newEndDate], [oldMode, oldStartDate, oldEndDate]) => {\r\n if (newMode !== oldMode || newStartDate !== oldStartDate || newEndDate !== oldEndDate) {\r\n setTimeLineHeaders(newMode);\r\n }\r\n });\r\n \r\n // 监听语言变化,重新生成时间轴表头\r\n watch(locale, () => {\r\n setTimeLineHeaders(mode.value);\r\n });\r\n\r\n watch(rootTask, (newVal) => { \r\n props.eventConfig.addRootTask(newVal);\r\n });\r\n\r\n watch(subTask, (newVal) => {\r\n props.eventConfig.addSubTask(newVal);\r\n });\r\n\r\n watch(removeTask, (newVal) => {\r\n props.eventConfig.removeTask(newVal);\r\n });\r\n\r\n watch(editTask, (newVal) => {\r\n props.eventConfig.editTask(newVal);\r\n });\r\n\r\n watch(allowChangeTaskDate, (newVal) => {\r\n props.eventConfig.allowChangeTaskDate(newVal);\r\n });\r\n\r\n // 优化:拆分watchEffect,避免不必要的触发\r\n watch(() => store.barDate, (barDate) => {\r\n if (barDate) {\r\n const { id, startDate, endDate } = barDate;\r\n if (id && startDate && endDate) {\r\n props.eventConfig.barDate(id, startDate, endDate);\r\n }\r\n }\r\n });\r\n\r\n watch(scale, (newScale) => {\r\n mutations.setScale(newScale);\r\n });\r\n\r\n watch(() => props.dataConfig.taskHeaders, (newHeaders) => {\r\n mutations.setTaskHeaders(newHeaders);\r\n });\r\n\r\n watch([monthHeaders, dayHeaders, weekHeaders, hourHeaders], ([newMonth, newDay, newWeek, newHour]) => {\r\n mutations.setMonthHeaders(newMonth);\r\n mutations.setDayHeaders(newDay);\r\n mutations.setWeekHeaders(newWeek);\r\n mutations.setHourHeaders(newHour);\r\n });\r\n\r\n watch(mode, (newMode) => {\r\n mutations.setMode(newMode);\r\n });\r\n\r\n watch(mapFields, (newFields) => {\r\n if (newFields) {\r\n mutations.setMapFields(newFields);\r\n }\r\n });\r\n\r\n watch(() => props.dataConfig.queryStartDate, (newStartDate) => {\r\n if (newStartDate) {\r\n mutations.setStartGanttDate(dayjs(newStartDate).toDate());\r\n startGanttDate.value = newStartDate;\r\n selectedStartDate.value = newStartDate;\r\n startDate.value = newStartDate;\r\n }\r\n });\r\n\r\n watch(() => props.dataConfig.queryEndDate, (newEndDate) => {\r\n if (newEndDate) {\r\n mutations.setEndGanttDate(dayjs(newEndDate).toDate());\r\n endGanttDate.value = newEndDate;\r\n selectedEndDate.value = newEndDate;\r\n endDate.value = newEndDate;\r\n }\r\n });\r\n\r\n watch(timelineCellCount, (newCount) => {\r\n if (newCount) {\r\n mutations.setTimelineCellCount(newCount);\r\n }\r\n });\r\n\r\n onBeforeMount(() => {\r\n mutations.setMonthHeaders(monthHeaders.value)\r\n mutations.setWeekHeaders(weekHeaders.value)\r\n mutations.setDayHeaders(dayHeaders.value)\r\n mutations.setHourHeaders(hourHeaders.value)\r\n mutations.setTaskHeaders(props.dataConfig.taskHeaders)\r\n mutations.setMapFields(mapFields.value)\r\n mutations.setTimelineCellCount(timelineCellCount.value)\r\n // 初始化时如果有数据,先设置\r\n if (dataSource.value && dataSource.value.length > 0) {\r\n mutations.setTasks(dataSource.value)\r\n }\r\n });\r\n\r\n // 进度更新事件处理\r\n const handleProgressUpdate = (event: Event) => {\r\n const customEvent = event as CustomEvent;\r\n if (props.eventConfig.updateProgress) {\r\n props.eventConfig.updateProgress(customEvent.detail);\r\n }\r\n };\r\n\r\n onMounted(() => {\r\n monthHeaders.value = [];\r\n weekHeaders.value = [];\r\n dayHeaders.value = [];\r\n hourHeaders.value = [];\r\n\r\n console.log('Gantt onMounted, dataSource:', dataSource.value);\r\n let level: number = 0;\r\n RecursionData('0', dataSource.value, level);\r\n mutations.setTasks(initData.value);\r\n console.log('initData after RecursionData:', initData.value);\r\n nextTick(() => {\r\n mode.value = '月';\r\n mutations.setMode(mode.value)\r\n });\r\n \r\n // 监听进度更新事件\r\n window.addEventListener('taskProgressUpdate', handleProgressUpdate);\r\n });\r\n \r\n // 优化:监听dataSource变化,使用节流避免频繁更新\r\n let updateTimer: ReturnType<typeof setTimeout> | null = null;\r\n watch(dataSource, (newVal) => {\r\n console.log('dataSource changed:', newVal);\r\n if (newVal && newVal.length > 0) {\r\n // 使用节流,避免频繁更新\r\n if (updateTimer) {\r\n clearTimeout(updateTimer);\r\n }\r\n updateTimer = setTimeout(() => {\r\n initData.value = [];\r\n let level: number = 0;\r\n RecursionData('0', newVal, level);\r\n mutations.setTasks(initData.value);\r\n console.log('Tasks updated:', initData.value);\r\n updateTimer = null;\r\n }, 100);\r\n } else if (newVal && newVal.length === 0) {\r\n // 如果数据为空,也要更新\r\n mutations.setTasks([]);\r\n }\r\n }, { immediate: false });\r\n\r\n onBeforeUnmount(() => {\r\n // 清理进度更新事件监听\r\n window.removeEventListener('taskProgressUpdate', handleProgressUpdate);\r\n });\r\n\r\n provide(Symbols.AddRootTaskSymbol, (row: Record<string, any>) => {\r\n return props.eventConfig.addRootTask(row);\r\n });\r\n\r\n // 设置Bar的颜色,传递到子组件\r\n provide(Symbols.SetBarColorSymbol, (row: Record<string, any>) => {\r\n return props.styleConfig.setBarColor(row);\r\n });\r\n \r\n // 提供甘特图容器引用给主题选择器\r\n provide('ganttContainer', ganttContainer);\r\n\r\n // 监听依赖关系变化\r\n watch(() => props.dataConfig.dependencies, (newDependencies) => {\r\n if (newDependencies && newDependencies.length > 0) {\r\n console.log('Loading dependencies:', newDependencies);\r\n // 清空现有依赖关系\r\n linkDataManager.clear();\r\n // 添加新的依赖关系\r\n newDependencies.forEach(dep => {\r\n linkDataManager.addDependency(dep);\r\n });\r\n }\r\n }, { immediate: true });\r\n\r\n // 连线配置变化处理\r\n const onLinkConfigChange = (config: any) => {\r\n console.log('连线配置已更新:', config);\r\n // 这里可以添加额外的处理逻辑,比如通知其他组件更新\r\n };\r\n\r\n // 连线类型颜色(从配置中读取,用于图例显示)\r\n const linkTypeColors = computed(() => ({\r\n finishToStart: linkConfig.linkTypeColors?.finishToStart || '#3498db',\r\n startToStart: linkConfig.linkTypeColors?.startToStart || '#2ecc71',\r\n finishToFinish: linkConfig.linkTypeColors?.finishToFinish || '#e74c3c',\r\n startToFinish: linkConfig.linkTypeColors?.startToFinish || '#f39c12'\r\n }));\r\n\r\n return {\r\n t,\r\n subTask,\r\n editTask,\r\n removeTask,\r\n rootTask,\r\n allowChangeTaskDate,\r\n paneLengthPercent,\r\n startDate,\r\n endDate,\r\n minStartDate,\r\n maxStartDate,\r\n confirmStart,\r\n minEndDate,\r\n maxEndDate,\r\n confirmEnd,\r\n buttonClass,\r\n timeMode,\r\n onLinkConfigChange,\r\n ganttContainer,\r\n linkTypeColors,\r\n linkTypeVisibility,\r\n updateLinkVisibility\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n$toolbarHeight: 70px;\r\n\r\n.page {\r\n height: 100%;\r\n width: 100%;\r\n overflow: hidden;\r\n display: flex;\r\n flex-direction: column;\r\n\r\n .toolbar {\r\n height: $toolbarHeight;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: row;\r\n align-items: center;\r\n justify-content: flex-start;\r\n padding: calc($toolbarHeight / 2);\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border-bottom: 1px solid var(--border, #d0d0d0);\r\n transition: all var(--transition-normal, 0.25s ease);\r\n\r\n .dateInput {\r\n cursor: pointer;\r\n height: calc($toolbarHeight / 1.5);\r\n width: 450px;\r\n display: flex;\r\n flex-direction: row;\r\n align-items: center;\r\n justify-content: center;\r\n margin-right: 20px;\r\n border-radius: 5px;\r\n color: var(--primary, #0078d4);\r\n font-size: 14px;\r\n transition: color var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n .buttonGroup {\r\n height: calc($toolbarHeight / 1.5);\r\n display: flex;\r\n flex-direction: row;\r\n margin-right: 20px;\r\n background: var(--bg-metal-normal);\r\n border: 1px solid var(--border);\r\n box-shadow: var(--shadow-inset), var(--shadow-outset);\r\n border-radius: 2px;\r\n overflow: hidden;\r\n transition: all var(--transition-normal);\r\n\r\n .metro-btn {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 80px;\r\n height: 100%;\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border-right: 1px solid var(--border, #d0d0d0);\r\n \r\n &:last-child {\r\n border-right: none;\r\n }\r\n\r\n &::before {\r\n content: '';\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f0f0f0));\r\n opacity: 0;\r\n transition: opacity var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n &::after {\r\n content: '';\r\n position: absolute;\r\n top: 2px;\r\n left: 2px;\r\n right: 2px;\r\n bottom: 2px;\r\n background: var(--bg-metal-pressed, linear-gradient(145deg, #e0e0e0, #f8f8f8));\r\n opacity: 0;\r\n transition: opacity var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n .metro-content {\r\n position: relative;\r\n z-index: 3;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 4px;\r\n }\r\n\r\n .metro-icon {\r\n color: var(--text-secondary, #666666);\r\n transition: color var(--transition-fast, 0.15s ease);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n\r\n .metro-text {\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--text-secondary, #555555);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n transition: color var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n &:hover {\r\n &::before {\r\n opacity: 1;\r\n }\r\n\r\n .metro-icon {\r\n color: var(--text-primary, #333333);\r\n }\r\n\r\n .metro-text {\r\n color: var(--text-primary, #333333);\r\n }\r\n }\r\n\r\n &:active {\r\n &::after {\r\n opacity: 1;\r\n }\r\n }\r\n\r\n // 激活状态\r\n &.button.is-active,\r\n &.is-active {\r\n background: linear-gradient(145deg, #0078d4, #106ebe);\r\n box-shadow: \r\n inset 0 1px 0 rgba(255, 255, 255, 0.2),\r\n inset 0 -1px 0 rgba(0, 0, 0, 0.3),\r\n 0 1px 2px rgba(0, 0, 0, 0.2);\r\n\r\n &::before {\r\n background: linear-gradient(145deg, #1084d8, #0d5aa7);\r\n opacity: 0;\r\n }\r\n\r\n &::after {\r\n background: linear-gradient(145deg, #0d5aa7, #1084d8);\r\n opacity: 0;\r\n }\r\n\r\n .metro-icon {\r\n color: #ffffff;\r\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);\r\n }\r\n\r\n .metro-text {\r\n color: #ffffff;\r\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);\r\n font-weight: 700;\r\n }\r\n\r\n &:hover {\r\n &::before {\r\n opacity: 1;\r\n }\r\n }\r\n\r\n &:active {\r\n &::after {\r\n opacity: 1;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // 兼容旧的class名称\r\n .button {\r\n @extend .metro-btn;\r\n }\r\n }\r\n \r\n .link-legend {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n padding: 6px 16px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n border-radius: 4px;\r\n margin-right: 16px;\r\n\r\n .legend-title {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--text-secondary, #666666);\r\n padding-right: 12px;\r\n border-right: 1px solid var(--border, #d0d0d0);\r\n \r\n svg {\r\n color: var(--primary, #0078d4);\r\n }\r\n }\r\n\r\n .legend-items {\r\n display: flex;\r\n gap: 12px;\r\n }\r\n\r\n .legend-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n cursor: pointer;\r\n padding: 2px 6px;\r\n border-radius: 3px;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n\r\n input[type=\"checkbox\"] {\r\n width: 14px;\r\n height: 14px;\r\n cursor: pointer;\r\n accent-color: var(--primary, #0078d4);\r\n margin: 0;\r\n }\r\n\r\n &:hover {\r\n background: var(--bg-secondary, #e8e8e8);\r\n }\r\n\r\n &.disabled {\r\n opacity: 0.5;\r\n \r\n svg {\r\n opacity: 0.4;\r\n }\r\n \r\n .legend-label,\r\n .legend-desc {\r\n text-decoration: line-through;\r\n }\r\n }\r\n\r\n .legend-label {\r\n font-size: 10px;\r\n font-weight: 700;\r\n color: var(--text-primary, #333333);\r\n min-width: 18px;\r\n }\r\n\r\n .legend-desc {\r\n font-size: 10px;\r\n color: var(--text-secondary, #666666);\r\n white-space: nowrap;\r\n }\r\n }\r\n }\r\n\r\n .config-buttons {\r\n display: flex;\r\n gap: 12px;\r\n margin-left: auto;\r\n padding: 0 8px;\r\n }\r\n \r\n .link-config-btn {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n padding: 10px 16px;\r\n background: linear-gradient(135deg, \r\n rgba(52, 152, 219, 0.1) 0%, \r\n rgba(155, 89, 182, 0.1) 50%, \r\n rgba(52, 152, 219, 0.1) 100%);\r\n border: 1px solid rgba(52, 152, 219, 0.3);\r\n border-radius: 8px;\r\n cursor: pointer;\r\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n overflow: hidden;\r\n \r\n &::before {\r\n content: '';\r\n position: absolute;\r\n top: 0;\r\n left: -100%;\r\n width: 100%;\r\n height: 100%;\r\n background: linear-gradient(90deg, \r\n transparent, \r\n rgba(255, 255, 255, 0.2), \r\n transparent);\r\n transition: left 0.5s ease;\r\n }\r\n \r\n .btn-content {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n z-index: 2;\r\n }\r\n \r\n .btn-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n \r\n svg {\r\n width: 18px;\r\n height: 18px;\r\n color: #3498db;\r\n transition: all 0.3s ease;\r\n filter: drop-shadow(0 0 3px rgba(52, 152, 219, 0.3));\r\n }\r\n }\r\n \r\n .btn-text {\r\n font-size: 13px;\r\n font-weight: 600;\r\n color: #2c3e50;\r\n transition: all 0.3s ease;\r\n }\r\n \r\n // 悬停效果\r\n &:hover {\r\n transform: translateY(-2px);\r\n background: linear-gradient(135deg, \r\n rgba(52, 152, 219, 0.2) 0%, \r\n rgba(155, 89, 182, 0.2) 50%, \r\n rgba(52, 152, 219, 0.2) 100%);\r\n border-color: rgba(52, 152, 219, 0.5);\r\n box-shadow: 0 4px 12px rgba(52, 152, 219, 0.2);\r\n \r\n &::before {\r\n left: 100%;\r\n }\r\n \r\n .btn-icon svg {\r\n color: #2980b9;\r\n transform: scale(1.1);\r\n }\r\n \r\n .btn-text {\r\n color: #2980b9;\r\n }\r\n }\r\n \r\n // 激活状态\r\n &.active {\r\n background: linear-gradient(135deg, \r\n rgba(231, 76, 60, 0.15) 0%, \r\n rgba(192, 57, 43, 0.15) 50%, \r\n rgba(231, 76, 60, 0.15) 100%);\r\n border-color: rgba(231, 76, 60, 0.4);\r\n box-shadow: 0 0 15px rgba(231, 76, 60, 0.3);\r\n \r\n .btn-icon svg {\r\n color: #e74c3c;\r\n animation: pulse 2s infinite ease-in-out;\r\n }\r\n \r\n .btn-text {\r\n color: #e74c3c;\r\n font-weight: 700;\r\n }\r\n }\r\n }\r\n \r\n // 动画定义\r\n @keyframes pulse {\r\n 0%, 100% {\r\n transform: scale(1);\r\n filter: drop-shadow(0 0 3px rgba(231, 76, 60, 0.3));\r\n }\r\n 50% {\r\n transform: scale(1.1);\r\n filter: drop-shadow(0 0 8px rgba(231, 76, 60, 0.6));\r\n }\r\n }\r\n }\r\n\r\n .gantt {\r\n height: calc(100% - #{$toolbarHeight});\r\n width: 100%;\r\n }\r\n \r\n .config-panel-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 1000;\r\n }\r\n \r\n .config-panel-container {\r\n position: relative;\r\n max-height: 90vh;\r\n overflow: hidden;\r\n }\r\n}\r\n</style>","<template>\r\n <div class=\"gantt-theme-selector\">\r\n <div class=\"theme-selector-trigger\" @click=\"toggleSelector\" :class=\"{ active: isOpen }\">\r\n <div class=\"theme-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z\"/>\r\n </svg>\r\n </div>\r\n <span class=\"theme-text\">主题</span>\r\n <div class=\"theme-arrow\" :class=\"{ rotated: isOpen }\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M7 10l5 5 5-5z\"/>\r\n </svg>\r\n </div>\r\n </div>\r\n\r\n <div class=\"theme-dropdown\" v-show=\"isOpen\" @click.stop>\r\n <div class=\"theme-dropdown-header\">\r\n <h3>选择主题</h3>\r\n <button class=\"close-btn\" @click=\"closeSelector\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <div class=\"theme-grid\">\r\n <div \r\n v-for=\"theme in availableThemes\" \r\n :key=\"theme.id\"\r\n class=\"theme-card\"\r\n :class=\"{ \r\n active: currentTheme === theme.id,\r\n preview: previewTheme === theme.id \r\n }\"\r\n @click=\"selectTheme(theme.id)\"\r\n @mouseenter=\"onPreviewTheme(theme.id)\"\r\n @mouseleave=\"onCancelPreview\"\r\n >\r\n <div class=\"theme-preview\" :style=\"{ backgroundColor: theme.preview }\">\r\n <div class=\"theme-preview-content\">\r\n <div class=\"preview-header\" :style=\"{ backgroundColor: theme.preview }\"></div>\r\n <div class=\"preview-body\">\r\n <div class=\"preview-bar\"></div>\r\n <div class=\"preview-bar short\"></div>\r\n <div class=\"preview-bar medium\"></div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"theme-info\">\r\n <h4 class=\"theme-name\">{{ t(theme.nameKey) }}</h4>\r\n <p class=\"theme-description\">{{ t(theme.descKey) }}</p>\r\n </div>\r\n <div class=\"theme-status\">\r\n <div v-if=\"currentTheme === theme.id\" class=\"status-badge current\">当前</div>\r\n <div v-else-if=\"previewTheme === theme.id\" class=\"status-badge preview\">预览</div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"theme-actions\">\r\n <button class=\"action-btn\" @click=\"exportConfig\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z\"/>\r\n </svg>\r\n 导出配置\r\n </button>\r\n <button class=\"action-btn\" @click=\"importConfig\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z\"/>\r\n </svg>\r\n 导入配置\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <input \r\n ref=\"fileInput\" \r\n type=\"file\" \r\n accept=\".json\" \r\n style=\"display: none\" \r\n @change=\"handleFileImport\"\r\n >\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, onMounted, onUnmounted, inject, watchEffect, nextTick } from 'vue';\r\nimport { ganttThemes, ganttThemeManager, type GanttTheme } from './themes/GanttThemes';\r\nimport { useI18n } from './i18n';\r\n\r\nexport default defineComponent({\r\n name: 'GanttThemeSelector',\r\n setup() {\r\n const { t } = useI18n();\r\n const isOpen = ref(false);\r\n const currentTheme = ref('metro');\r\n const previewTheme = ref('');\r\n const fileInput = ref<HTMLInputElement>();\r\n const availableThemes = ref<GanttTheme[]>(ganttThemes);\r\n\r\n // 获取甘特图容器引用\r\n const ganttContainer = inject<any>('ganttContainer');\r\n\r\n const toggleSelector = () => {\r\n isOpen.value = !isOpen.value;\r\n };\r\n\r\n const closeSelector = () => {\r\n isOpen.value = false;\r\n onCancelPreview();\r\n };\r\n\r\n const selectTheme = (themeId: string) => {\r\n console.log('🎯 selectTheme called with:', themeId);\r\n console.log('📦 ganttContainer:', ganttContainer);\r\n console.log('📦 ganttContainer.value:', ganttContainer?.value);\r\n \r\n currentTheme.value = themeId;\r\n previewTheme.value = '';\r\n \r\n // 先尝试注入样式,不依赖容器引用\r\n injectThemeStyles(themeId);\r\n \r\n // 查找甘特图容器的多种方式\r\n let container = null;\r\n \r\n if (ganttContainer && ganttContainer.value) {\r\n container = ganttContainer.value;\r\n console.log('✅ Using injected container reference');\r\n } else {\r\n // 备用方案:直接查找DOM元素\r\n container = document.querySelector('.gantt-container') || \r\n document.querySelector('.page') ||\r\n document.querySelector('[class*=\"gantt\"]');\r\n console.log('🔍 Using DOM query fallback, found:', container);\r\n }\r\n \r\n if (container) {\r\n // 设置容器属性\r\n container.setAttribute('data-gantt-theme', themeId);\r\n console.log('✅ Container attribute set:', container.getAttribute('data-gantt-theme'));\r\n \r\n // 同时设置到页面根元素\r\n const pageElement = document.querySelector('.page');\r\n if (pageElement) {\r\n pageElement.setAttribute('data-gantt-theme', themeId);\r\n console.log('✅ Page element attribute set');\r\n }\r\n \r\n // 保存到localStorage\r\n try {\r\n localStorage.setItem('gantt-theme', themeId);\r\n console.log('💾 Theme saved to localStorage:', themeId);\r\n } catch (error) {\r\n console.warn('Failed to save theme:', error);\r\n }\r\n \r\n // 强制触发重绘\r\n setTimeout(() => {\r\n console.log('🔄 Forcing style recalculation...');\r\n if (container) {\r\n container.style.display = 'none';\r\n container.offsetHeight; // 触发重排\r\n container.style.display = '';\r\n }\r\n }, 50);\r\n \r\n } else {\r\n console.error('❌ No gantt container found! Available elements:');\r\n console.log('Available .page elements:', document.querySelectorAll('.page'));\r\n console.log('Available .gantt-container elements:', document.querySelectorAll('.gantt-container'));\r\n console.log('Available elements with gantt in class:', document.querySelectorAll('[class*=\"gantt\"]'));\r\n }\r\n \r\n closeSelector();\r\n };\r\n\r\n const onPreviewTheme = (themeId: string) => {\r\n if (themeId !== currentTheme.value && ganttContainer && ganttContainer.value) {\r\n previewTheme.value = themeId;\r\n \r\n // 临时注入预览主题\r\n injectThemeStyles(themeId);\r\n }\r\n };\r\n\r\n const onCancelPreview = () => {\r\n if (previewTheme.value && ganttContainer && ganttContainer.value) {\r\n previewTheme.value = '';\r\n \r\n // 恢复当前主题\r\n injectThemeStyles(currentTheme.value);\r\n }\r\n };\r\n\r\n const exportConfig = () => {\r\n const config = ganttThemeManager.exportThemeConfig();\r\n const blob = new Blob([config], { type: 'application/json' });\r\n const url = URL.createObjectURL(blob);\r\n const a = document.createElement('a');\r\n a.href = url;\r\n a.download = `gantt-theme-config-${new Date().toISOString().split('T')[0]}.json`;\r\n document.body.appendChild(a);\r\n a.click();\r\n document.body.removeChild(a);\r\n URL.revokeObjectURL(url);\r\n };\r\n\r\n const importConfig = () => {\r\n fileInput.value?.click();\r\n };\r\n\r\n const handleFileImport = (event: Event) => {\r\n const file = (event.target as HTMLInputElement).files?.[0];\r\n if (file) {\r\n const reader = new FileReader();\r\n reader.onload = (e) => {\r\n try {\r\n const content = e.target?.result as string;\r\n if (ganttThemeManager.importThemeConfig(content)) {\r\n currentTheme.value = ganttThemeManager.getCurrentTheme();\r\n alert('主题配置导入成功!');\r\n } else {\r\n alert('主题配置文件格式错误!');\r\n }\r\n } catch (error) {\r\n alert('主题配置文件格式错误!');\r\n }\r\n };\r\n reader.readAsText(file);\r\n }\r\n };\r\n\r\n const handleClickOutside = (event: MouseEvent) => {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.gantt-theme-selector')) {\r\n closeSelector();\r\n }\r\n };\r\n\r\n // 动态注入主题样式 - 增强版\r\n const injectThemeStyles = (themeId: string) => {\r\n console.log('🎨 Starting theme injection for:', themeId);\r\n \r\n // 移除之前的主题样式\r\n const existingStyle = document.getElementById('gantt-theme-styles');\r\n if (existingStyle) {\r\n existingStyle.remove();\r\n console.log('🗑️ Removed existing theme styles');\r\n }\r\n \r\n const theme = ganttThemes.find(t => t.id === themeId);\r\n if (!theme) {\r\n console.error('❌ Theme not found:', themeId);\r\n return;\r\n }\r\n \r\n console.log('📋 Theme found:', t(theme.nameKey), 'Variables count:', Object.keys(theme.cssVariables).length);\r\n \r\n // 创建样式元素\r\n const style = document.createElement('style');\r\n style.id = 'gantt-theme-styles';\r\n \r\n // 生成CSS规则\r\n const cssRules = Object.entries(theme.cssVariables)\r\n .map(([property, value]) => `${property}: ${value};`)\r\n .join('\\n ');\r\n \r\n // 创建更强力的CSS内容,使用!important确保优先级\r\n style.textContent = `\r\n /* 甘特图主题样式 - ${themeId} */\r\n .page.gantt-container,\r\n .page.gantt-container *,\r\n .gantt-container,\r\n .gantt-container *,\r\n [data-gantt-theme=\"${themeId}\"],\r\n [data-gantt-theme=\"${themeId}\"] *,\r\n .toolbar,\r\n .toolbar *,\r\n .buttonGroup,\r\n .buttonGroup *,\r\n .metro-btn,\r\n .metro-btn *,\r\n .gantt,\r\n .gantt * {\r\n ${cssRules}\r\n }\r\n \r\n /* 根级别变量 */\r\n :root {\r\n ${cssRules}\r\n }\r\n \r\n /* 强制应用到特定元素 */\r\n .page[data-gantt-theme=\"${themeId}\"] .toolbar {\r\n background: var(--bg-metal-normal) !important;\r\n border-bottom: 1px solid var(--border) !important;\r\n }\r\n \r\n .page[data-gantt-theme=\"${themeId}\"] .buttonGroup {\r\n background: var(--bg-metal-normal) !important;\r\n border: 1px solid var(--border) !important;\r\n }\r\n \r\n .page[data-gantt-theme=\"${themeId}\"] .metro-btn {\r\n background: var(--bg-metal-normal) !important;\r\n color: var(--text-secondary) !important;\r\n border-right: 1px solid var(--border) !important;\r\n }\r\n \r\n .page[data-gantt-theme=\"${themeId}\"] .metro-btn.is-active,\r\n .page[data-gantt-theme=\"${themeId}\"] .metro-btn.button.is-active {\r\n background: var(--bg-active) !important;\r\n color: var(--text-white) !important;\r\n }\r\n `;\r\n \r\n document.head.appendChild(style);\r\n console.log('✅ Theme styles injected successfully');\r\n console.log('📊 Injected CSS length:', style.textContent.length, 'characters');\r\n \r\n // 验证样式是否正确注入\r\n const injectedStyle = document.getElementById('gantt-theme-styles');\r\n if (injectedStyle) {\r\n console.log('✅ Style element found in DOM');\r\n } else {\r\n console.error('❌ Style element not found in DOM');\r\n }\r\n };\r\n\r\n // 初始化主题 - 增强版\r\n const initTheme = () => {\r\n console.log('🚀 initTheme called');\r\n console.log('📦 ganttContainer:', ganttContainer);\r\n console.log('📦 ganttContainer.value:', ganttContainer?.value);\r\n \r\n // 从localStorage加载保存的主题\r\n try {\r\n const savedTheme = localStorage.getItem('gantt-theme');\r\n if (savedTheme && ganttThemes.find(t => t.id === savedTheme)) {\r\n currentTheme.value = savedTheme;\r\n console.log('💾 Loaded theme from localStorage:', savedTheme);\r\n }\r\n } catch (error) {\r\n console.warn('Failed to load theme from localStorage:', error);\r\n }\r\n \r\n console.log('🎨 Current theme:', currentTheme.value);\r\n \r\n // 先注入样式\r\n injectThemeStyles(currentTheme.value);\r\n \r\n // 查找并设置容器属性\r\n let container = null;\r\n \r\n if (ganttContainer && ganttContainer.value) {\r\n container = ganttContainer.value;\r\n console.log('✅ Using injected container reference');\r\n } else {\r\n // 备用方案:直接查找DOM元素\r\n container = document.querySelector('.gantt-container') || \r\n document.querySelector('.page') ||\r\n document.querySelector('[class*=\"gantt\"]');\r\n console.log('🔍 Using DOM query fallback, found:', container);\r\n }\r\n \r\n if (container) {\r\n container.setAttribute('data-gantt-theme', currentTheme.value);\r\n console.log('✅ Container attribute set to:', currentTheme.value);\r\n \r\n // 同时设置到页面根元素\r\n const pageElement = document.querySelector('.page');\r\n if (pageElement) {\r\n pageElement.setAttribute('data-gantt-theme', currentTheme.value);\r\n console.log('✅ Page element attribute set');\r\n }\r\n } else {\r\n console.warn('⚠️ No container found during initialization');\r\n // 延迟重试\r\n setTimeout(() => {\r\n console.log('🔄 Retrying theme initialization...');\r\n initTheme();\r\n }, 500);\r\n }\r\n \r\n console.log('✅ Theme initialization completed');\r\n };\r\n\r\n // 使用watchEffect监听容器变化\r\n watchEffect(() => {\r\n if (ganttContainer && ganttContainer.value) {\r\n nextTick(() => {\r\n initTheme();\r\n });\r\n }\r\n });\r\n\r\n onMounted(() => {\r\n document.addEventListener('click', handleClickOutside);\r\n // 如果容器还没准备好,延迟初始化\r\n if (!ganttContainer || !ganttContainer.value) {\r\n setTimeout(() => {\r\n initTheme();\r\n }, 200);\r\n }\r\n });\r\n\r\n onUnmounted(() => {\r\n document.removeEventListener('click', handleClickOutside);\r\n onCancelPreview();\r\n });\r\n\r\n return {\r\n t,\r\n availableThemes,\r\n isOpen,\r\n currentTheme,\r\n previewTheme,\r\n fileInput,\r\n toggleSelector,\r\n closeSelector,\r\n selectTheme,\r\n onPreviewTheme,\r\n onCancelPreview,\r\n exportConfig,\r\n importConfig,\r\n handleFileImport\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n.gantt-theme-selector {\r\n position: relative;\r\n display: inline-block;\r\n}\r\n\r\n.theme-selector-trigger {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 8px 12px;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border: 1px solid var(--border, #d0d0d0);\r\n box-shadow: var(--shadow-inset, inset 0 1px 0 rgba(255, 255, 255, 0.8)), var(--shadow-outset, 0 2px 4px rgba(0, 0, 0, 0.1));\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n font-family: var(--font-family, 'Segoe UI', sans-serif);\r\n font-size: var(--font-size-base, 12px);\r\n font-weight: var(--font-weight-bold, 600);\r\n color: var(--text-secondary, #666666);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.theme-selector-trigger:hover {\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n color: var(--text-primary, #333333);\r\n}\r\n\r\n.theme-selector-trigger.active {\r\n background: var(--bg-active, linear-gradient(145deg, #0078d4, #106ebe));\r\n color: var(--text-white, #ffffff);\r\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.theme-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.theme-arrow {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: transform var(--transition-fast, 0.15s ease);\r\n}\r\n\r\n.theme-arrow.rotated {\r\n transform: rotate(180deg);\r\n}\r\n\r\n.theme-dropdown {\r\n position: absolute;\r\n top: 100%;\r\n right: 0;\r\n z-index: 1000;\r\n width: 480px;\r\n max-height: 600px;\r\n background: var(--bg-content, #ffffff);\r\n border: 1px solid var(--border, #d0d0d0);\r\n box-shadow: var(--shadow-outset, 0 2px 4px rgba(0, 0, 0, 0.1)), 0 8px 24px rgba(0, 0, 0, 0.15);\r\n overflow: hidden;\r\n margin-top: 4px;\r\n}\r\n\r\n.theme-dropdown-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 16px 20px;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border-bottom: 1px solid var(--border, #d0d0d0);\r\n}\r\n\r\n.theme-dropdown-header h3 {\r\n margin: 0;\r\n font-size: 16px;\r\n font-weight: var(--font-weight-bold, 600);\r\n color: var(--text-primary, #333333);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.close-btn {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 24px;\r\n height: 24px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n color: var(--text-secondary, #666666);\r\n}\r\n\r\n.close-btn:hover {\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n color: var(--text-primary, #333333);\r\n}\r\n\r\n.theme-grid {\r\n display: grid;\r\n grid-template-columns: repeat(2, 1fr);\r\n gap: 16px;\r\n padding: 20px;\r\n max-height: 400px;\r\n overflow-y: auto;\r\n}\r\n\r\n.theme-card {\r\n position: relative;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n border-radius: 8px;\r\n overflow: hidden;\r\n cursor: pointer;\r\n transition: all var(--transition-normal, 0.25s ease);\r\n}\r\n\r\n.theme-card:hover {\r\n transform: translateY(-2px);\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\r\n}\r\n\r\n.theme-card.active {\r\n border-color: var(--primary, #0078d4);\r\n box-shadow: 0 0 0 2px rgba(0, 120, 212, 0.2);\r\n}\r\n\r\n.theme-card.preview {\r\n border-color: var(--secondary, #0d5aa7);\r\n box-shadow: 0 0 0 2px rgba(13, 90, 167, 0.2);\r\n}\r\n\r\n.theme-preview {\r\n height: 80px;\r\n position: relative;\r\n overflow: hidden;\r\n}\r\n\r\n.theme-preview-content {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.preview-header {\r\n height: 20px;\r\n opacity: 0.9;\r\n}\r\n\r\n.preview-body {\r\n flex: 1;\r\n padding: 8px;\r\n background: rgba(255, 255, 255, 0.9);\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n}\r\n\r\n.preview-bar {\r\n height: 6px;\r\n background: rgba(0, 0, 0, 0.1);\r\n border-radius: 3px;\r\n}\r\n\r\n.preview-bar.short {\r\n width: 60%;\r\n}\r\n\r\n.preview-bar.medium {\r\n width: 80%;\r\n}\r\n\r\n.theme-info {\r\n padding: 12px;\r\n}\r\n\r\n.theme-name {\r\n margin: 0 0 4px 0;\r\n font-size: 14px;\r\n font-weight: var(--font-weight-bold, 600);\r\n color: var(--text-primary, #333333);\r\n}\r\n\r\n.theme-description {\r\n margin: 0;\r\n font-size: 12px;\r\n color: var(--text-secondary, #666666);\r\n line-height: 1.4;\r\n}\r\n\r\n.theme-status {\r\n position: absolute;\r\n top: 8px;\r\n right: 8px;\r\n}\r\n\r\n.status-badge {\r\n padding: 2px 6px;\r\n font-size: 10px;\r\n font-weight: var(--font-weight-bold, 600);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n border-radius: 10px;\r\n}\r\n\r\n.status-badge.current {\r\n background: var(--primary, #0078d4);\r\n color: var(--text-white, #ffffff);\r\n}\r\n\r\n.status-badge.preview {\r\n background: var(--secondary, #0d5aa7);\r\n color: var(--text-white, #ffffff);\r\n}\r\n\r\n.theme-actions {\r\n display: flex;\r\n gap: 8px;\r\n padding: 16px 20px;\r\n background: var(--bg-secondary, #e8e8e8);\r\n border-top: 1px solid var(--border, #d0d0d0);\r\n}\r\n\r\n.action-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 8px 12px;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border: 1px solid var(--border, #d0d0d0);\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n font-family: var(--font-family, 'Segoe UI', sans-serif);\r\n font-size: 11px;\r\n font-weight: var(--font-weight-bold, 600);\r\n color: var(--text-secondary, #666666);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.action-btn:hover {\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n color: var(--text-primary, #333333);\r\n}\r\n\r\n/* 滚动条样式 */\r\n.theme-grid::-webkit-scrollbar {\r\n width: 8px;\r\n}\r\n\r\n.theme-grid::-webkit-scrollbar-track {\r\n background: var(--bg-secondary, #e8e8e8);\r\n}\r\n\r\n.theme-grid::-webkit-scrollbar-thumb {\r\n background: var(--border, #d0d0d0);\r\n border-radius: 4px;\r\n}\r\n\r\n.theme-grid::-webkit-scrollbar-thumb:hover {\r\n background: var(--primary, #0078d4);\r\n}\r\n</style>","<template>\r\n <div class=\"gantt-theme-selector\">\r\n <div class=\"theme-selector-trigger\" @click=\"toggleSelector\" :class=\"{ active: isOpen }\">\r\n <div class=\"theme-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z\"/>\r\n </svg>\r\n </div>\r\n <span class=\"theme-text\">主题</span>\r\n <div class=\"theme-arrow\" :class=\"{ rotated: isOpen }\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M7 10l5 5 5-5z\"/>\r\n </svg>\r\n </div>\r\n </div>\r\n\r\n <div class=\"theme-dropdown\" v-show=\"isOpen\" @click.stop>\r\n <div class=\"theme-dropdown-header\">\r\n <h3>选择主题</h3>\r\n <button class=\"close-btn\" @click=\"closeSelector\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <div class=\"theme-grid\">\r\n <div \r\n v-for=\"theme in availableThemes\" \r\n :key=\"theme.id\"\r\n class=\"theme-card\"\r\n :class=\"{ \r\n active: currentTheme === theme.id,\r\n preview: previewTheme === theme.id \r\n }\"\r\n @click=\"selectTheme(theme.id)\"\r\n @mouseenter=\"onPreviewTheme(theme.id)\"\r\n @mouseleave=\"onCancelPreview\"\r\n >\r\n <div class=\"theme-preview\" :style=\"{ backgroundColor: theme.preview }\">\r\n <div class=\"theme-preview-content\">\r\n <div class=\"preview-header\" :style=\"{ backgroundColor: theme.preview }\"></div>\r\n <div class=\"preview-body\">\r\n <div class=\"preview-bar\"></div>\r\n <div class=\"preview-bar short\"></div>\r\n <div class=\"preview-bar medium\"></div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"theme-info\">\r\n <h4 class=\"theme-name\">{{ t(theme.nameKey) }}</h4>\r\n <p class=\"theme-description\">{{ t(theme.descKey) }}</p>\r\n </div>\r\n <div class=\"theme-status\">\r\n <div v-if=\"currentTheme === theme.id\" class=\"status-badge current\">当前</div>\r\n <div v-else-if=\"previewTheme === theme.id\" class=\"status-badge preview\">预览</div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"theme-actions\">\r\n <button class=\"action-btn\" @click=\"exportConfig\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z\"/>\r\n </svg>\r\n 导出配置\r\n </button>\r\n <button class=\"action-btn\" @click=\"importConfig\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z\"/>\r\n </svg>\r\n 导入配置\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <input \r\n ref=\"fileInput\" \r\n type=\"file\" \r\n accept=\".json\" \r\n style=\"display: none\" \r\n @change=\"handleFileImport\"\r\n >\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, onMounted, onUnmounted, inject, watchEffect, nextTick } from 'vue';\r\nimport { ganttThemes, ganttThemeManager, type GanttTheme } from './themes/GanttThemes';\r\nimport { useI18n } from './i18n';\r\n\r\nexport default defineComponent({\r\n name: 'GanttThemeSelector',\r\n setup() {\r\n const { t } = useI18n();\r\n const isOpen = ref(false);\r\n const currentTheme = ref('metro');\r\n const previewTheme = ref('');\r\n const fileInput = ref<HTMLInputElement>();\r\n const availableThemes = ref<GanttTheme[]>(ganttThemes);\r\n\r\n // 获取甘特图容器引用\r\n const ganttContainer = inject<any>('ganttContainer');\r\n\r\n const toggleSelector = () => {\r\n isOpen.value = !isOpen.value;\r\n };\r\n\r\n const closeSelector = () => {\r\n isOpen.value = false;\r\n onCancelPreview();\r\n };\r\n\r\n const selectTheme = (themeId: string) => {\r\n console.log('🎯 selectTheme called with:', themeId);\r\n console.log('📦 ganttContainer:', ganttContainer);\r\n console.log('📦 ganttContainer.value:', ganttContainer?.value);\r\n \r\n currentTheme.value = themeId;\r\n previewTheme.value = '';\r\n \r\n // 先尝试注入样式,不依赖容器引用\r\n injectThemeStyles(themeId);\r\n \r\n // 查找甘特图容器的多种方式\r\n let container = null;\r\n \r\n if (ganttContainer && ganttContainer.value) {\r\n container = ganttContainer.value;\r\n console.log('✅ Using injected container reference');\r\n } else {\r\n // 备用方案:直接查找DOM元素\r\n container = document.querySelector('.gantt-container') || \r\n document.querySelector('.page') ||\r\n document.querySelector('[class*=\"gantt\"]');\r\n console.log('🔍 Using DOM query fallback, found:', container);\r\n }\r\n \r\n if (container) {\r\n // 设置容器属性\r\n container.setAttribute('data-gantt-theme', themeId);\r\n console.log('✅ Container attribute set:', container.getAttribute('data-gantt-theme'));\r\n \r\n // 同时设置到页面根元素\r\n const pageElement = document.querySelector('.page');\r\n if (pageElement) {\r\n pageElement.setAttribute('data-gantt-theme', themeId);\r\n console.log('✅ Page element attribute set');\r\n }\r\n \r\n // 保存到localStorage\r\n try {\r\n localStorage.setItem('gantt-theme', themeId);\r\n console.log('💾 Theme saved to localStorage:', themeId);\r\n } catch (error) {\r\n console.warn('Failed to save theme:', error);\r\n }\r\n \r\n // 强制触发重绘\r\n setTimeout(() => {\r\n console.log('🔄 Forcing style recalculation...');\r\n if (container) {\r\n container.style.display = 'none';\r\n container.offsetHeight; // 触发重排\r\n container.style.display = '';\r\n }\r\n }, 50);\r\n \r\n } else {\r\n console.error('❌ No gantt container found! Available elements:');\r\n console.log('Available .page elements:', document.querySelectorAll('.page'));\r\n console.log('Available .gantt-container elements:', document.querySelectorAll('.gantt-container'));\r\n console.log('Available elements with gantt in class:', document.querySelectorAll('[class*=\"gantt\"]'));\r\n }\r\n \r\n closeSelector();\r\n };\r\n\r\n const onPreviewTheme = (themeId: string) => {\r\n if (themeId !== currentTheme.value && ganttContainer && ganttContainer.value) {\r\n previewTheme.value = themeId;\r\n \r\n // 临时注入预览主题\r\n injectThemeStyles(themeId);\r\n }\r\n };\r\n\r\n const onCancelPreview = () => {\r\n if (previewTheme.value && ganttContainer && ganttContainer.value) {\r\n previewTheme.value = '';\r\n \r\n // 恢复当前主题\r\n injectThemeStyles(currentTheme.value);\r\n }\r\n };\r\n\r\n const exportConfig = () => {\r\n const config = ganttThemeManager.exportThemeConfig();\r\n const blob = new Blob([config], { type: 'application/json' });\r\n const url = URL.createObjectURL(blob);\r\n const a = document.createElement('a');\r\n a.href = url;\r\n a.download = `gantt-theme-config-${new Date().toISOString().split('T')[0]}.json`;\r\n document.body.appendChild(a);\r\n a.click();\r\n document.body.removeChild(a);\r\n URL.revokeObjectURL(url);\r\n };\r\n\r\n const importConfig = () => {\r\n fileInput.value?.click();\r\n };\r\n\r\n const handleFileImport = (event: Event) => {\r\n const file = (event.target as HTMLInputElement).files?.[0];\r\n if (file) {\r\n const reader = new FileReader();\r\n reader.onload = (e) => {\r\n try {\r\n const content = e.target?.result as string;\r\n if (ganttThemeManager.importThemeConfig(content)) {\r\n currentTheme.value = ganttThemeManager.getCurrentTheme();\r\n alert('主题配置导入成功!');\r\n } else {\r\n alert('主题配置文件格式错误!');\r\n }\r\n } catch (error) {\r\n alert('主题配置文件格式错误!');\r\n }\r\n };\r\n reader.readAsText(file);\r\n }\r\n };\r\n\r\n const handleClickOutside = (event: MouseEvent) => {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.gantt-theme-selector')) {\r\n closeSelector();\r\n }\r\n };\r\n\r\n // 动态注入主题样式 - 增强版\r\n const injectThemeStyles = (themeId: string) => {\r\n console.log('🎨 Starting theme injection for:', themeId);\r\n \r\n // 移除之前的主题样式\r\n const existingStyle = document.getElementById('gantt-theme-styles');\r\n if (existingStyle) {\r\n existingStyle.remove();\r\n console.log('🗑️ Removed existing theme styles');\r\n }\r\n \r\n const theme = ganttThemes.find(t => t.id === themeId);\r\n if (!theme) {\r\n console.error('❌ Theme not found:', themeId);\r\n return;\r\n }\r\n \r\n console.log('📋 Theme found:', t(theme.nameKey), 'Variables count:', Object.keys(theme.cssVariables).length);\r\n \r\n // 创建样式元素\r\n const style = document.createElement('style');\r\n style.id = 'gantt-theme-styles';\r\n \r\n // 生成CSS规则\r\n const cssRules = Object.entries(theme.cssVariables)\r\n .map(([property, value]) => `${property}: ${value};`)\r\n .join('\\n ');\r\n \r\n // 创建更强力的CSS内容,使用!important确保优先级\r\n style.textContent = `\r\n /* 甘特图主题样式 - ${themeId} */\r\n .page.gantt-container,\r\n .page.gantt-container *,\r\n .gantt-container,\r\n .gantt-container *,\r\n [data-gantt-theme=\"${themeId}\"],\r\n [data-gantt-theme=\"${themeId}\"] *,\r\n .toolbar,\r\n .toolbar *,\r\n .buttonGroup,\r\n .buttonGroup *,\r\n .metro-btn,\r\n .metro-btn *,\r\n .gantt,\r\n .gantt * {\r\n ${cssRules}\r\n }\r\n \r\n /* 根级别变量 */\r\n :root {\r\n ${cssRules}\r\n }\r\n \r\n /* 强制应用到特定元素 */\r\n .page[data-gantt-theme=\"${themeId}\"] .toolbar {\r\n background: var(--bg-metal-normal) !important;\r\n border-bottom: 1px solid var(--border) !important;\r\n }\r\n \r\n .page[data-gantt-theme=\"${themeId}\"] .buttonGroup {\r\n background: var(--bg-metal-normal) !important;\r\n border: 1px solid var(--border) !important;\r\n }\r\n \r\n .page[data-gantt-theme=\"${themeId}\"] .metro-btn {\r\n background: var(--bg-metal-normal) !important;\r\n color: var(--text-secondary) !important;\r\n border-right: 1px solid var(--border) !important;\r\n }\r\n \r\n .page[data-gantt-theme=\"${themeId}\"] .metro-btn.is-active,\r\n .page[data-gantt-theme=\"${themeId}\"] .metro-btn.button.is-active {\r\n background: var(--bg-active) !important;\r\n color: var(--text-white) !important;\r\n }\r\n `;\r\n \r\n document.head.appendChild(style);\r\n console.log('✅ Theme styles injected successfully');\r\n console.log('📊 Injected CSS length:', style.textContent.length, 'characters');\r\n \r\n // 验证样式是否正确注入\r\n const injectedStyle = document.getElementById('gantt-theme-styles');\r\n if (injectedStyle) {\r\n console.log('✅ Style element found in DOM');\r\n } else {\r\n console.error('❌ Style element not found in DOM');\r\n }\r\n };\r\n\r\n // 初始化主题 - 增强版\r\n const initTheme = () => {\r\n console.log('🚀 initTheme called');\r\n console.log('📦 ganttContainer:', ganttContainer);\r\n console.log('📦 ganttContainer.value:', ganttContainer?.value);\r\n \r\n // 从localStorage加载保存的主题\r\n try {\r\n const savedTheme = localStorage.getItem('gantt-theme');\r\n if (savedTheme && ganttThemes.find(t => t.id === savedTheme)) {\r\n currentTheme.value = savedTheme;\r\n console.log('💾 Loaded theme from localStorage:', savedTheme);\r\n }\r\n } catch (error) {\r\n console.warn('Failed to load theme from localStorage:', error);\r\n }\r\n \r\n console.log('🎨 Current theme:', currentTheme.value);\r\n \r\n // 先注入样式\r\n injectThemeStyles(currentTheme.value);\r\n \r\n // 查找并设置容器属性\r\n let container = null;\r\n \r\n if (ganttContainer && ganttContainer.value) {\r\n container = ganttContainer.value;\r\n console.log('✅ Using injected container reference');\r\n } else {\r\n // 备用方案:直接查找DOM元素\r\n container = document.querySelector('.gantt-container') || \r\n document.querySelector('.page') ||\r\n document.querySelector('[class*=\"gantt\"]');\r\n console.log('🔍 Using DOM query fallback, found:', container);\r\n }\r\n \r\n if (container) {\r\n container.setAttribute('data-gantt-theme', currentTheme.value);\r\n console.log('✅ Container attribute set to:', currentTheme.value);\r\n \r\n // 同时设置到页面根元素\r\n const pageElement = document.querySelector('.page');\r\n if (pageElement) {\r\n pageElement.setAttribute('data-gantt-theme', currentTheme.value);\r\n console.log('✅ Page element attribute set');\r\n }\r\n } else {\r\n console.warn('⚠️ No container found during initialization');\r\n // 延迟重试\r\n setTimeout(() => {\r\n console.log('🔄 Retrying theme initialization...');\r\n initTheme();\r\n }, 500);\r\n }\r\n \r\n console.log('✅ Theme initialization completed');\r\n };\r\n\r\n // 使用watchEffect监听容器变化\r\n watchEffect(() => {\r\n if (ganttContainer && ganttContainer.value) {\r\n nextTick(() => {\r\n initTheme();\r\n });\r\n }\r\n });\r\n\r\n onMounted(() => {\r\n document.addEventListener('click', handleClickOutside);\r\n // 如果容器还没准备好,延迟初始化\r\n if (!ganttContainer || !ganttContainer.value) {\r\n setTimeout(() => {\r\n initTheme();\r\n }, 200);\r\n }\r\n });\r\n\r\n onUnmounted(() => {\r\n document.removeEventListener('click', handleClickOutside);\r\n onCancelPreview();\r\n });\r\n\r\n return {\r\n t,\r\n availableThemes,\r\n isOpen,\r\n currentTheme,\r\n previewTheme,\r\n fileInput,\r\n toggleSelector,\r\n closeSelector,\r\n selectTheme,\r\n onPreviewTheme,\r\n onCancelPreview,\r\n exportConfig,\r\n importConfig,\r\n handleFileImport\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n.gantt-theme-selector {\r\n position: relative;\r\n display: inline-block;\r\n}\r\n\r\n.theme-selector-trigger {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 8px 12px;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border: 1px solid var(--border, #d0d0d0);\r\n box-shadow: var(--shadow-inset, inset 0 1px 0 rgba(255, 255, 255, 0.8)), var(--shadow-outset, 0 2px 4px rgba(0, 0, 0, 0.1));\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n font-family: var(--font-family, 'Segoe UI', sans-serif);\r\n font-size: var(--font-size-base, 12px);\r\n font-weight: var(--font-weight-bold, 600);\r\n color: var(--text-secondary, #666666);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.theme-selector-trigger:hover {\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n color: var(--text-primary, #333333);\r\n}\r\n\r\n.theme-selector-trigger.active {\r\n background: var(--bg-active, linear-gradient(145deg, #0078d4, #106ebe));\r\n color: var(--text-white, #ffffff);\r\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.theme-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.theme-arrow {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: transform var(--transition-fast, 0.15s ease);\r\n}\r\n\r\n.theme-arrow.rotated {\r\n transform: rotate(180deg);\r\n}\r\n\r\n.theme-dropdown {\r\n position: absolute;\r\n top: 100%;\r\n right: 0;\r\n z-index: 1000;\r\n width: 480px;\r\n max-height: 600px;\r\n background: var(--bg-content, #ffffff);\r\n border: 1px solid var(--border, #d0d0d0);\r\n box-shadow: var(--shadow-outset, 0 2px 4px rgba(0, 0, 0, 0.1)), 0 8px 24px rgba(0, 0, 0, 0.15);\r\n overflow: hidden;\r\n margin-top: 4px;\r\n}\r\n\r\n.theme-dropdown-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 16px 20px;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border-bottom: 1px solid var(--border, #d0d0d0);\r\n}\r\n\r\n.theme-dropdown-header h3 {\r\n margin: 0;\r\n font-size: 16px;\r\n font-weight: var(--font-weight-bold, 600);\r\n color: var(--text-primary, #333333);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.close-btn {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 24px;\r\n height: 24px;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n color: var(--text-secondary, #666666);\r\n}\r\n\r\n.close-btn:hover {\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n color: var(--text-primary, #333333);\r\n}\r\n\r\n.theme-grid {\r\n display: grid;\r\n grid-template-columns: repeat(2, 1fr);\r\n gap: 16px;\r\n padding: 20px;\r\n max-height: 400px;\r\n overflow-y: auto;\r\n}\r\n\r\n.theme-card {\r\n position: relative;\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n border: 1px solid var(--border, #d0d0d0);\r\n border-radius: 8px;\r\n overflow: hidden;\r\n cursor: pointer;\r\n transition: all var(--transition-normal, 0.25s ease);\r\n}\r\n\r\n.theme-card:hover {\r\n transform: translateY(-2px);\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\r\n}\r\n\r\n.theme-card.active {\r\n border-color: var(--primary, #0078d4);\r\n box-shadow: 0 0 0 2px rgba(0, 120, 212, 0.2);\r\n}\r\n\r\n.theme-card.preview {\r\n border-color: var(--secondary, #0d5aa7);\r\n box-shadow: 0 0 0 2px rgba(13, 90, 167, 0.2);\r\n}\r\n\r\n.theme-preview {\r\n height: 80px;\r\n position: relative;\r\n overflow: hidden;\r\n}\r\n\r\n.theme-preview-content {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.preview-header {\r\n height: 20px;\r\n opacity: 0.9;\r\n}\r\n\r\n.preview-body {\r\n flex: 1;\r\n padding: 8px;\r\n background: rgba(255, 255, 255, 0.9);\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n}\r\n\r\n.preview-bar {\r\n height: 6px;\r\n background: rgba(0, 0, 0, 0.1);\r\n border-radius: 3px;\r\n}\r\n\r\n.preview-bar.short {\r\n width: 60%;\r\n}\r\n\r\n.preview-bar.medium {\r\n width: 80%;\r\n}\r\n\r\n.theme-info {\r\n padding: 12px;\r\n}\r\n\r\n.theme-name {\r\n margin: 0 0 4px 0;\r\n font-size: 14px;\r\n font-weight: var(--font-weight-bold, 600);\r\n color: var(--text-primary, #333333);\r\n}\r\n\r\n.theme-description {\r\n margin: 0;\r\n font-size: 12px;\r\n color: var(--text-secondary, #666666);\r\n line-height: 1.4;\r\n}\r\n\r\n.theme-status {\r\n position: absolute;\r\n top: 8px;\r\n right: 8px;\r\n}\r\n\r\n.status-badge {\r\n padding: 2px 6px;\r\n font-size: 10px;\r\n font-weight: var(--font-weight-bold, 600);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n border-radius: 10px;\r\n}\r\n\r\n.status-badge.current {\r\n background: var(--primary, #0078d4);\r\n color: var(--text-white, #ffffff);\r\n}\r\n\r\n.status-badge.preview {\r\n background: var(--secondary, #0d5aa7);\r\n color: var(--text-white, #ffffff);\r\n}\r\n\r\n.theme-actions {\r\n display: flex;\r\n gap: 8px;\r\n padding: 16px 20px;\r\n background: var(--bg-secondary, #e8e8e8);\r\n border-top: 1px solid var(--border, #d0d0d0);\r\n}\r\n\r\n.action-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 8px 12px;\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border: 1px solid var(--border, #d0d0d0);\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n font-family: var(--font-family, 'Segoe UI', sans-serif);\r\n font-size: 11px;\r\n font-weight: var(--font-weight-bold, 600);\r\n color: var(--text-secondary, #666666);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.action-btn:hover {\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n color: var(--text-primary, #333333);\r\n}\r\n\r\n/* 滚动条样式 */\r\n.theme-grid::-webkit-scrollbar {\r\n width: 8px;\r\n}\r\n\r\n.theme-grid::-webkit-scrollbar-track {\r\n background: var(--bg-secondary, #e8e8e8);\r\n}\r\n\r\n.theme-grid::-webkit-scrollbar-thumb {\r\n background: var(--border, #d0d0d0);\r\n border-radius: 4px;\r\n}\r\n\r\n.theme-grid::-webkit-scrollbar-thumb:hover {\r\n background: var(--primary, #0078d4);\r\n}\r\n</style>","<template>\r\n <div class=\"language-selector\">\r\n <button \r\n class=\"lang-btn\" \r\n @click=\"toggleDropdown\"\r\n :class=\"{ active: isOpen }\"\r\n :title=\"t('configPanel.languageSettings')\"\r\n >\r\n <div class=\"btn-content\">\r\n <div class=\"btn-icon\">\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z\"/>\r\n </svg>\r\n </div>\r\n <span class=\"btn-text\">{{ currentLocaleLabel }}</span>\r\n <div class=\"btn-arrow\" :class=\"{ rotated: isOpen }\">\r\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M7 10l5 5 5-5z\"/>\r\n </svg>\r\n </div>\r\n </div>\r\n </button>\r\n\r\n <transition name=\"dropdown-fade\">\r\n <div v-if=\"isOpen\" class=\"lang-dropdown\">\r\n <div\r\n v-for=\"locale in locales\"\r\n :key=\"locale.value\"\r\n class=\"lang-option\"\r\n :class=\"{ active: currentLocale === locale.value }\"\r\n @click=\"selectLocale(locale.value)\"\r\n >\r\n <span class=\"lang-label\">{{ locale.label }}</span>\r\n <div v-if=\"currentLocale === locale.value\" class=\"lang-check\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/>\r\n </svg>\r\n </div>\r\n </div>\r\n </div>\r\n </transition>\r\n\r\n <div v-if=\"isOpen\" class=\"dropdown-overlay\" @click=\"closeDropdown\"></div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, computed, onMounted, onUnmounted } from 'vue';\r\nimport { useI18n, type Locale } from './i18n';\r\n\r\nexport default defineComponent({\r\n name: 'LanguageSelector',\r\n setup() {\r\n const { locale, t, setLocale, getLocales } = useI18n();\r\n const isOpen = ref(false);\r\n\r\n const currentLocale = computed(() => locale.value);\r\n const locales = computed(() => getLocales());\r\n \r\n const currentLocaleLabel = computed(() => {\r\n const current = locales.value.find((l: { value: Locale; label: string }) => l.value === currentLocale.value);\r\n return current?.label || 'Language';\r\n });\r\n\r\n const toggleDropdown = () => {\r\n isOpen.value = !isOpen.value;\r\n };\r\n\r\n const closeDropdown = () => {\r\n isOpen.value = false;\r\n };\r\n\r\n const selectLocale = (localeValue: Locale) => {\r\n setLocale(localeValue);\r\n closeDropdown();\r\n };\r\n\r\n // 点击外部关闭下拉菜单\r\n const handleClickOutside = (event: MouseEvent) => {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.language-selector')) {\r\n closeDropdown();\r\n }\r\n };\r\n\r\n onMounted(() => {\r\n document.addEventListener('click', handleClickOutside);\r\n });\r\n\r\n onUnmounted(() => {\r\n document.removeEventListener('click', handleClickOutside);\r\n });\r\n\r\n return {\r\n isOpen,\r\n currentLocale,\r\n currentLocaleLabel,\r\n locales,\r\n t,\r\n toggleDropdown,\r\n closeDropdown,\r\n selectLocale\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.language-selector {\r\n position: relative;\r\n}\r\n\r\n.lang-btn {\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border: 1px solid var(--border, #d0d0d0);\r\n box-shadow: var(--shadow-inset, inset 0 1px 0 rgba(255, 255, 255, 0.8));\r\n padding: 8px 12px;\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n font-family: var(--font-family);\r\n display: flex;\r\n align-items: center;\r\n min-width: 140px;\r\n\r\n &:hover {\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n }\r\n\r\n &:active,\r\n &.active {\r\n background: var(--bg-active, linear-gradient(145deg, #0078d4, #106ebe));\r\n\r\n .btn-icon,\r\n .btn-text,\r\n .btn-arrow {\r\n color: var(--text-white, #ffffff);\r\n }\r\n }\r\n\r\n .btn-content {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n width: 100%;\r\n }\r\n\r\n .btn-icon {\r\n color: var(--text-secondary, #666666);\r\n display: flex;\r\n align-items: center;\r\n transition: color var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n .btn-text {\r\n flex: 1;\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--text-primary, #333333);\r\n transition: color var(--transition-fast, 0.15s ease);\r\n text-align: left;\r\n }\r\n\r\n .btn-arrow {\r\n color: var(--text-secondary, #666666);\r\n display: flex;\r\n align-items: center;\r\n transition: transform var(--transition-fast, 0.15s ease), color var(--transition-fast, 0.15s ease);\r\n\r\n &.rotated {\r\n transform: rotate(180deg);\r\n }\r\n }\r\n}\r\n\r\n.dropdown-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n z-index: 998;\r\n}\r\n\r\n.lang-dropdown {\r\n position: absolute;\r\n top: calc(100% + 4px);\r\n left: 0;\r\n right: 0;\r\n background: var(--bg-content, #ffffff);\r\n border: 1px solid var(--border, #d0d0d0);\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\r\n z-index: 999;\r\n min-width: 140px;\r\n color: var(--text-primary, #333333);\r\n}\r\n\r\n.lang-option {\r\n padding: 10px 12px;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n border-bottom: 1px solid var(--border, #e8e8e8);\r\n color: var(--text-primary, #333333);\r\n\r\n &:last-child {\r\n border-bottom: none;\r\n }\r\n\r\n &:hover {\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n }\r\n\r\n &.active {\r\n background: var(--bg-active-hover, linear-gradient(145deg, #e3f2fd, #bbdefb));\r\n color: var(--primary, #0078d4);\r\n font-weight: 600;\r\n }\r\n\r\n .lang-label {\r\n font-size: 12px;\r\n }\r\n\r\n .lang-check {\r\n color: var(--primary, #0078d4);\r\n display: flex;\r\n align-items: center;\r\n }\r\n}\r\n\r\n.dropdown-fade-enter-active,\r\n.dropdown-fade-leave-active {\r\n transition: opacity 0.15s ease, transform 0.15s ease;\r\n}\r\n\r\n.dropdown-fade-enter-from {\r\n opacity: 0;\r\n transform: translateY(-8px);\r\n}\r\n\r\n.dropdown-fade-leave-to {\r\n opacity: 0;\r\n transform: translateY(-8px);\r\n}\r\n</style>\r\n","<template>\r\n <div class=\"language-selector\">\r\n <button \r\n class=\"lang-btn\" \r\n @click=\"toggleDropdown\"\r\n :class=\"{ active: isOpen }\"\r\n :title=\"t('configPanel.languageSettings')\"\r\n >\r\n <div class=\"btn-content\">\r\n <div class=\"btn-icon\">\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z\"/>\r\n </svg>\r\n </div>\r\n <span class=\"btn-text\">{{ currentLocaleLabel }}</span>\r\n <div class=\"btn-arrow\" :class=\"{ rotated: isOpen }\">\r\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M7 10l5 5 5-5z\"/>\r\n </svg>\r\n </div>\r\n </div>\r\n </button>\r\n\r\n <transition name=\"dropdown-fade\">\r\n <div v-if=\"isOpen\" class=\"lang-dropdown\">\r\n <div\r\n v-for=\"locale in locales\"\r\n :key=\"locale.value\"\r\n class=\"lang-option\"\r\n :class=\"{ active: currentLocale === locale.value }\"\r\n @click=\"selectLocale(locale.value)\"\r\n >\r\n <span class=\"lang-label\">{{ locale.label }}</span>\r\n <div v-if=\"currentLocale === locale.value\" class=\"lang-check\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/>\r\n </svg>\r\n </div>\r\n </div>\r\n </div>\r\n </transition>\r\n\r\n <div v-if=\"isOpen\" class=\"dropdown-overlay\" @click=\"closeDropdown\"></div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, computed, onMounted, onUnmounted } from 'vue';\r\nimport { useI18n, type Locale } from './i18n';\r\n\r\nexport default defineComponent({\r\n name: 'LanguageSelector',\r\n setup() {\r\n const { locale, t, setLocale, getLocales } = useI18n();\r\n const isOpen = ref(false);\r\n\r\n const currentLocale = computed(() => locale.value);\r\n const locales = computed(() => getLocales());\r\n \r\n const currentLocaleLabel = computed(() => {\r\n const current = locales.value.find((l: { value: Locale; label: string }) => l.value === currentLocale.value);\r\n return current?.label || 'Language';\r\n });\r\n\r\n const toggleDropdown = () => {\r\n isOpen.value = !isOpen.value;\r\n };\r\n\r\n const closeDropdown = () => {\r\n isOpen.value = false;\r\n };\r\n\r\n const selectLocale = (localeValue: Locale) => {\r\n setLocale(localeValue);\r\n closeDropdown();\r\n };\r\n\r\n // 点击外部关闭下拉菜单\r\n const handleClickOutside = (event: MouseEvent) => {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.language-selector')) {\r\n closeDropdown();\r\n }\r\n };\r\n\r\n onMounted(() => {\r\n document.addEventListener('click', handleClickOutside);\r\n });\r\n\r\n onUnmounted(() => {\r\n document.removeEventListener('click', handleClickOutside);\r\n });\r\n\r\n return {\r\n isOpen,\r\n currentLocale,\r\n currentLocaleLabel,\r\n locales,\r\n t,\r\n toggleDropdown,\r\n closeDropdown,\r\n selectLocale\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.language-selector {\r\n position: relative;\r\n}\r\n\r\n.lang-btn {\r\n background: var(--bg-metal-normal, linear-gradient(145deg, #f5f5f5, #e8e8e8));\r\n border: 1px solid var(--border, #d0d0d0);\r\n box-shadow: var(--shadow-inset, inset 0 1px 0 rgba(255, 255, 255, 0.8));\r\n padding: 8px 12px;\r\n cursor: pointer;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n font-family: var(--font-family);\r\n display: flex;\r\n align-items: center;\r\n min-width: 140px;\r\n\r\n &:hover {\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n }\r\n\r\n &:active,\r\n &.active {\r\n background: var(--bg-active, linear-gradient(145deg, #0078d4, #106ebe));\r\n\r\n .btn-icon,\r\n .btn-text,\r\n .btn-arrow {\r\n color: var(--text-white, #ffffff);\r\n }\r\n }\r\n\r\n .btn-content {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n width: 100%;\r\n }\r\n\r\n .btn-icon {\r\n color: var(--text-secondary, #666666);\r\n display: flex;\r\n align-items: center;\r\n transition: color var(--transition-fast, 0.15s ease);\r\n }\r\n\r\n .btn-text {\r\n flex: 1;\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--text-primary, #333333);\r\n transition: color var(--transition-fast, 0.15s ease);\r\n text-align: left;\r\n }\r\n\r\n .btn-arrow {\r\n color: var(--text-secondary, #666666);\r\n display: flex;\r\n align-items: center;\r\n transition: transform var(--transition-fast, 0.15s ease), color var(--transition-fast, 0.15s ease);\r\n\r\n &.rotated {\r\n transform: rotate(180deg);\r\n }\r\n }\r\n}\r\n\r\n.dropdown-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n z-index: 998;\r\n}\r\n\r\n.lang-dropdown {\r\n position: absolute;\r\n top: calc(100% + 4px);\r\n left: 0;\r\n right: 0;\r\n background: var(--bg-content, #ffffff);\r\n border: 1px solid var(--border, #d0d0d0);\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\r\n z-index: 999;\r\n min-width: 140px;\r\n color: var(--text-primary, #333333);\r\n}\r\n\r\n.lang-option {\r\n padding: 10px 12px;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n transition: all var(--transition-fast, 0.15s ease);\r\n border-bottom: 1px solid var(--border, #e8e8e8);\r\n color: var(--text-primary, #333333);\r\n\r\n &:last-child {\r\n border-bottom: none;\r\n }\r\n\r\n &:hover {\r\n background: var(--bg-metal-light, linear-gradient(145deg, #ffffff, #f5f5f5));\r\n }\r\n\r\n &.active {\r\n background: var(--bg-active-hover, linear-gradient(145deg, #e3f2fd, #bbdefb));\r\n color: var(--primary, #0078d4);\r\n font-weight: 600;\r\n }\r\n\r\n .lang-label {\r\n font-size: 12px;\r\n }\r\n\r\n .lang-check {\r\n color: var(--primary, #0078d4);\r\n display: flex;\r\n align-items: center;\r\n }\r\n}\r\n\r\n.dropdown-fade-enter-active,\r\n.dropdown-fade-leave-active {\r\n transition: opacity 0.15s ease, transform 0.15s ease;\r\n}\r\n\r\n.dropdown-fade-enter-from {\r\n opacity: 0;\r\n transform: translateY(-8px);\r\n}\r\n\r\n.dropdown-fade-leave-to {\r\n opacity: 0;\r\n transform: translateY(-8px);\r\n}\r\n</style>\r\n","<template>\r\n <div class=\"link-config-panel\">\r\n <div class=\"panel-header\">\r\n <h3>连线配置</h3>\r\n <button class=\"close-btn\" @click=\"$emit('close')\" title=\"关闭\">×</button>\r\n </div>\r\n \r\n <div class=\"panel-content\">\r\n <!-- 路径类型选择 -->\r\n <div class=\"config-section\">\r\n <h4>路径类型</h4>\r\n <div class=\"path-type-selector\">\r\n <label \r\n v-for=\"pathType in pathTypes\" \r\n :key=\"pathType.value\"\r\n class=\"path-type-option\"\r\n :class=\"{ active: config.pathType === pathType.value }\"\r\n >\r\n <input \r\n type=\"radio\" \r\n :value=\"pathType.value\" \r\n v-model=\"config.pathType\"\r\n @change=\"updateConfig\"\r\n />\r\n <div class=\"path-preview\">\r\n <svg width=\"60\" height=\"40\" viewBox=\"0 0 60 40\">\r\n <path \r\n :d=\"pathType.preview\" \r\n stroke=\"#3498db\" \r\n stroke-width=\"2\" \r\n fill=\"none\"\r\n />\r\n </svg>\r\n </div>\r\n <span>{{ pathType.name }}</span>\r\n </label>\r\n </div>\r\n </div>\r\n \r\n <!-- 基础样式配置 -->\r\n <div class=\"config-section\">\r\n <h4>基础样式</h4>\r\n <div class=\"config-row\">\r\n <label>颜色:</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"config.color\" \r\n @change=\"updateConfig\"\r\n class=\"color-input\"\r\n />\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>线宽:</label>\r\n <input \r\n type=\"range\" \r\n min=\"1\" \r\n max=\"5\" \r\n step=\"0.5\"\r\n v-model.number=\"config.width\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.width }}px</span>\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>虚线样式:</label>\r\n <select v-model=\"config.dashArray\" @change=\"updateConfig\" class=\"select-input\">\r\n <option :value=\"undefined\">实线</option>\r\n <option value=\"3,3\">短虚线</option>\r\n <option value=\"5,5\">中虚线</option>\r\n <option value=\"8,4\">长虚线</option>\r\n <option value=\"2,2,8,2\">点划线</option>\r\n </select>\r\n </div>\r\n </div>\r\n \r\n <!-- 路径特定配置 -->\r\n <div class=\"config-section\" v-if=\"config.pathType === 'bezier'\">\r\n <h4>贝塞尔曲线配置</h4>\r\n <div class=\"config-row\">\r\n <label>弯曲度:</label>\r\n <input \r\n type=\"range\" \r\n min=\"0.1\" \r\n max=\"1\" \r\n step=\"0.1\"\r\n v-model.number=\"config.bezierCurvature\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.bezierCurvature }}</span>\r\n </div>\r\n </div>\r\n \r\n <div class=\"config-section\" v-if=\"config.pathType === 'right-angle'\">\r\n <h4>直角连线配置</h4>\r\n <div class=\"config-row\">\r\n <label>偏移距离:</label>\r\n <input \r\n type=\"range\" \r\n min=\"10\" \r\n max=\"80\" \r\n step=\"5\"\r\n v-model.number=\"config.rightAngleOffset\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.rightAngleOffset }}px</span>\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"config.smoothCorners\" \r\n @change=\"updateConfig\"\r\n />\r\n 平滑转角\r\n </label>\r\n </div>\r\n \r\n <div class=\"config-row\" v-if=\"config.smoothCorners\">\r\n <label>转角半径:</label>\r\n <input \r\n type=\"range\" \r\n min=\"0\" \r\n max=\"15\" \r\n step=\"1\"\r\n v-model.number=\"config.cornerRadius\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.cornerRadius }}px</span>\r\n </div>\r\n </div>\r\n \r\n <!-- 箭头配置 -->\r\n <div class=\"config-section\">\r\n <h4>箭头配置</h4>\r\n <div class=\"config-row\">\r\n <label>\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"config.showArrow\" \r\n @change=\"updateConfig\"\r\n />\r\n 显示箭头\r\n </label>\r\n </div>\r\n \r\n <div v-if=\"config.showArrow\">\r\n <div class=\"config-row\">\r\n <label>箭头大小:</label>\r\n <input \r\n type=\"range\" \r\n min=\"4\" \r\n max=\"16\" \r\n step=\"1\"\r\n v-model.number=\"config.arrowSize\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.arrowSize }}px</span>\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>箭头颜色:</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"config.arrowColor\" \r\n @change=\"updateConfig\"\r\n class=\"color-input\"\r\n :placeholder=\"config.color\"\r\n />\r\n <button \r\n @click=\"config.arrowColor = config.color; updateConfig()\" \r\n class=\"sync-btn\"\r\n title=\"与线条颜色同步\"\r\n >\r\n 同步\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- 虚线动画配置 -->\r\n <div class=\"config-section\">\r\n <h4>虚线动画</h4>\r\n <div class=\"config-row\">\r\n <label>\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"config.enableDashAnimation\" \r\n @change=\"updateConfig\"\r\n />\r\n 启用虚线流动动画\r\n </label>\r\n </div>\r\n \r\n <div v-if=\"config.enableDashAnimation\">\r\n <div class=\"config-row\">\r\n <label>动画速度:</label>\r\n <input \r\n type=\"range\" \r\n min=\"0.2\" \r\n max=\"3\" \r\n step=\"0.2\"\r\n v-model.number=\"config.dashAnimationSpeed\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.dashAnimationSpeed }}s</span>\r\n </div>\r\n \r\n <div class=\"config-info\">\r\n <small>💡 虚线动画只在连线设置为虚线样式时显示</small>\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- 标签配置 -->\r\n <div class=\"config-section\">\r\n <h4>标签配置</h4>\r\n <div class=\"config-row\">\r\n <label>\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"config.showLabels\" \r\n @change=\"updateConfig\"\r\n />\r\n 显示标签\r\n </label>\r\n </div>\r\n \r\n <div v-if=\"config.showLabels\">\r\n <div class=\"config-row\">\r\n <label>标签颜色:</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"config.labelColor\" \r\n @change=\"updateConfig\"\r\n class=\"color-input\"\r\n />\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>字体大小:</label>\r\n <input \r\n type=\"range\" \r\n min=\"8\" \r\n max=\"16\" \r\n step=\"1\"\r\n v-model.number=\"config.labelFontSize\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.labelFontSize }}px</span>\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- 父子关系样式 -->\r\n <div class=\"config-section\">\r\n <h4>父子关系样式</h4>\r\n <div class=\"config-row\">\r\n <label>颜色:</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"config.parentChildStyle.color\" \r\n @change=\"updateConfig\"\r\n class=\"color-input\"\r\n />\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>线宽:</label>\r\n <input \r\n type=\"range\" \r\n min=\"1\" \r\n max=\"3\" \r\n step=\"0.5\"\r\n v-model.number=\"config.parentChildStyle.width\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.parentChildStyle.width }}px</span>\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>虚线样式:</label>\r\n <select v-model=\"config.parentChildStyle.dashArray\" @change=\"updateConfig\" class=\"select-input\">\r\n <option :value=\"undefined\">实线</option>\r\n <option value=\"3,3\">短虚线</option>\r\n <option value=\"5,5\">中虚线</option>\r\n <option value=\"8,4\">长虚线</option>\r\n </select>\r\n </div>\r\n </div>\r\n \r\n <!-- 预设主题 -->\r\n <div class=\"config-section\">\r\n <h4>预设主题</h4>\r\n <div class=\"theme-selector\">\r\n <button \r\n v-for=\"(theme, name) in themes\" \r\n :key=\"name\"\r\n @click=\"applyTheme(name)\"\r\n class=\"theme-btn\"\r\n :class=\"{ active: isCurrentTheme(name) }\"\r\n >\r\n {{ getThemeName(name) }}\r\n </button>\r\n </div>\r\n </div>\r\n \r\n <!-- 操作按钮 -->\r\n <div class=\"config-section\">\r\n <div class=\"action-buttons\">\r\n <button @click=\"resetConfig\" class=\"reset-btn\">重置为默认</button>\r\n <button @click=\"exportConfig\" class=\"export-btn\">导出配置</button>\r\n <button @click=\"showImportDialog = true\" class=\"import-btn\">导入配置</button>\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- 导入配置对话框 -->\r\n <div v-if=\"showImportDialog\" class=\"import-dialog-overlay\" @click=\"showImportDialog = false\">\r\n <div class=\"import-dialog\" @click.stop>\r\n <h4>导入配置</h4>\r\n <textarea \r\n v-model=\"importText\" \r\n placeholder=\"粘贴配置JSON...\"\r\n class=\"import-textarea\"\r\n ></textarea>\r\n <div class=\"import-actions\">\r\n <button @click=\"importConfig\" class=\"confirm-btn\">导入</button>\r\n <button @click=\"showImportDialog = false\" class=\"cancel-btn\">取消</button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, computed } from 'vue';\r\nimport { LinkPathType, LinkThemes, useLinkConfig } from './LinkConfig';\r\n\r\nexport default defineComponent({\r\n name: 'LinkConfigPanel',\r\n emits: ['close', 'configChange'],\r\n setup(_, { emit }) {\r\n const { \r\n config, \r\n setTheme, \r\n updateConfig: updateLinkConfig, \r\n reset, \r\n exportConfig: exportLinkConfig, \r\n importConfig: importLinkConfig,\r\n themes \r\n } = useLinkConfig();\r\n \r\n const showImportDialog = ref(false);\r\n const importText = ref('');\r\n \r\n // 路径类型选项\r\n const pathTypes = [\r\n {\r\n value: LinkPathType.STRAIGHT,\r\n name: '直线',\r\n preview: 'M 10 20 L 50 20'\r\n },\r\n {\r\n value: LinkPathType.BEZIER,\r\n name: '贝塞尔曲线',\r\n preview: 'M 10 20 C 25 20 35 20 50 20'\r\n },\r\n {\r\n value: LinkPathType.RIGHT_ANGLE,\r\n name: '直角连线',\r\n preview: 'M 10 20 L 30 20 L 30 30 L 50 30'\r\n }\r\n ];\r\n \r\n // 主题名称映射\r\n const themeNames = {\r\n default: '默认',\r\n business: '商务',\r\n modern: '现代',\r\n minimal: '简约',\r\n colorful: '彩色'\r\n };\r\n \r\n const getThemeName = (name: string) => {\r\n return themeNames[name as keyof typeof themeNames] || name;\r\n };\r\n \r\n // 检查是否为当前主题\r\n const isCurrentTheme = (themeName: string) => {\r\n const theme = themes[themeName as keyof typeof themes];\r\n if (!theme) return false;\r\n \r\n return Object.keys(theme).every(key => {\r\n const themeValue = theme[key as keyof typeof theme];\r\n const configValue = config[key as keyof typeof config];\r\n \r\n if (typeof themeValue === 'object' && typeof configValue === 'object') {\r\n return JSON.stringify(themeValue) === JSON.stringify(configValue);\r\n }\r\n \r\n return themeValue === configValue;\r\n });\r\n };\r\n \r\n // 更新配置\r\n const updateConfig = () => {\r\n updateLinkConfig(config);\r\n emit('configChange', config);\r\n };\r\n \r\n // 应用主题\r\n const applyTheme = (themeName: string) => {\r\n setTheme(themeName as keyof typeof LinkThemes);\r\n emit('configChange', config);\r\n };\r\n \r\n // 重置配置\r\n const resetConfig = () => {\r\n reset();\r\n emit('configChange', config);\r\n };\r\n \r\n // 导出配置\r\n const exportConfig = () => {\r\n const configJson = exportLinkConfig();\r\n navigator.clipboard.writeText(configJson).then(() => {\r\n alert('配置已复制到剪贴板');\r\n }).catch(() => {\r\n // 降级方案:显示配置内容\r\n const textarea = document.createElement('textarea');\r\n textarea.value = configJson;\r\n document.body.appendChild(textarea);\r\n textarea.select();\r\n document.execCommand('copy');\r\n document.body.removeChild(textarea);\r\n alert('配置已复制到剪贴板');\r\n });\r\n };\r\n \r\n // 导入配置\r\n const importConfig = () => {\r\n try {\r\n const success = importLinkConfig(importText.value);\r\n if (success) {\r\n showImportDialog.value = false;\r\n importText.value = '';\r\n emit('configChange', config);\r\n alert('配置导入成功');\r\n } else {\r\n alert('配置格式错误,请检查JSON格式');\r\n }\r\n } catch (error) {\r\n alert('配置导入失败:' + error);\r\n }\r\n };\r\n \r\n return {\r\n config,\r\n pathTypes,\r\n themes,\r\n showImportDialog,\r\n importText,\r\n getThemeName,\r\n isCurrentTheme,\r\n updateConfig,\r\n applyTheme,\r\n resetConfig,\r\n exportConfig,\r\n importConfig\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.link-config-panel {\r\n width: 320px;\r\n background: white;\r\n border: 1px solid #ddd;\r\n border-radius: 8px;\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\r\n max-height: 80vh;\r\n overflow: hidden;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.panel-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 16px 20px;\r\n border-bottom: 1px solid #eee;\r\n background: #f8f9fa;\r\n \r\n h3 {\r\n margin: 0;\r\n font-size: 16px;\r\n font-weight: 600;\r\n color: #333;\r\n }\r\n \r\n .close-btn {\r\n background: none;\r\n border: none;\r\n font-size: 20px;\r\n cursor: pointer;\r\n color: #666;\r\n padding: 0;\r\n width: 24px;\r\n height: 24px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n border-radius: 4px;\r\n \r\n &:hover {\r\n background: #e9ecef;\r\n color: #333;\r\n }\r\n }\r\n}\r\n\r\n.panel-content {\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 16px 20px;\r\n}\r\n\r\n.config-section {\r\n margin-bottom: 24px;\r\n \r\n h4 {\r\n margin: 0 0 12px 0;\r\n font-size: 14px;\r\n font-weight: 600;\r\n color: #495057;\r\n }\r\n}\r\n\r\n.path-type-selector {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n.path-type-option {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n padding: 8px 12px;\r\n border: 2px solid #e9ecef;\r\n border-radius: 6px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n \r\n &:hover {\r\n border-color: #3498db;\r\n background: #f8f9ff;\r\n }\r\n \r\n &.active {\r\n border-color: #3498db;\r\n background: #e3f2fd;\r\n }\r\n \r\n input[type=\"radio\"] {\r\n display: none;\r\n }\r\n \r\n .path-preview {\r\n flex-shrink: 0;\r\n }\r\n \r\n span {\r\n font-size: 13px;\r\n color: #495057;\r\n }\r\n}\r\n\r\n.config-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n margin-bottom: 12px;\r\n \r\n label {\r\n font-size: 13px;\r\n color: #495057;\r\n min-width: 80px;\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n }\r\n \r\n .value {\r\n font-size: 12px;\r\n color: #6c757d;\r\n min-width: 40px;\r\n text-align: right;\r\n }\r\n}\r\n\r\n.color-input {\r\n width: 40px;\r\n height: 32px;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n padding: 0;\r\n \r\n &::-webkit-color-swatch-wrapper {\r\n padding: 0;\r\n }\r\n \r\n &::-webkit-color-swatch {\r\n border: none;\r\n border-radius: 3px;\r\n }\r\n}\r\n\r\n.range-input {\r\n flex: 1;\r\n height: 6px;\r\n background: #e9ecef;\r\n border-radius: 3px;\r\n outline: none;\r\n \r\n &::-webkit-slider-thumb {\r\n appearance: none;\r\n width: 16px;\r\n height: 16px;\r\n background: #3498db;\r\n border-radius: 50%;\r\n cursor: pointer;\r\n }\r\n \r\n &::-moz-range-thumb {\r\n width: 16px;\r\n height: 16px;\r\n background: #3498db;\r\n border-radius: 50%;\r\n cursor: pointer;\r\n border: none;\r\n }\r\n}\r\n\r\n.select-input {\r\n flex: 1;\r\n padding: 6px 8px;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n font-size: 13px;\r\n background: white;\r\n \r\n &:focus {\r\n outline: none;\r\n border-color: #3498db;\r\n }\r\n}\r\n\r\n.sync-btn {\r\n padding: 4px 8px;\r\n font-size: 11px;\r\n background: #f8f9fa;\r\n border: 1px solid #ddd;\r\n border-radius: 3px;\r\n cursor: pointer;\r\n \r\n &:hover {\r\n background: #e9ecef;\r\n }\r\n}\r\n\r\n.theme-selector {\r\n display: grid;\r\n grid-template-columns: 1fr 1fr;\r\n gap: 8px;\r\n}\r\n\r\n.theme-btn {\r\n padding: 8px 12px;\r\n font-size: 12px;\r\n background: #f8f9fa;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n \r\n &:hover {\r\n background: #e9ecef;\r\n border-color: #adb5bd;\r\n }\r\n \r\n &.active {\r\n background: #3498db;\r\n color: white;\r\n border-color: #3498db;\r\n }\r\n}\r\n\r\n.action-buttons {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n.reset-btn, .export-btn, .import-btn {\r\n padding: 8px 16px;\r\n font-size: 13px;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.reset-btn {\r\n background: #f8f9fa;\r\n color: #495057;\r\n \r\n &:hover {\r\n background: #e9ecef;\r\n }\r\n}\r\n\r\n.export-btn {\r\n background: #28a745;\r\n color: white;\r\n border-color: #28a745;\r\n \r\n &:hover {\r\n background: #218838;\r\n }\r\n}\r\n\r\n.import-btn {\r\n background: #17a2b8;\r\n color: white;\r\n border-color: #17a2b8;\r\n \r\n &:hover {\r\n background: #138496;\r\n }\r\n}\r\n\r\n.import-dialog-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 1000;\r\n}\r\n\r\n.import-dialog {\r\n background: white;\r\n border-radius: 8px;\r\n padding: 20px;\r\n width: 400px;\r\n max-width: 90vw;\r\n \r\n h4 {\r\n margin: 0 0 16px 0;\r\n font-size: 16px;\r\n color: #333;\r\n }\r\n}\r\n\r\n.import-textarea {\r\n width: 100%;\r\n height: 120px;\r\n padding: 8px;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n font-family: monospace;\r\n font-size: 12px;\r\n resize: vertical;\r\n \r\n &:focus {\r\n outline: none;\r\n border-color: #3498db;\r\n }\r\n}\r\n\r\n.import-actions {\r\n display: flex;\r\n gap: 8px;\r\n margin-top: 16px;\r\n justify-content: flex-end;\r\n}\r\n\r\n.confirm-btn {\r\n padding: 8px 16px;\r\n background: #28a745;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n \r\n &:hover {\r\n background: #218838;\r\n }\r\n}\r\n\r\n.cancel-btn {\r\n padding: 8px 16px;\r\n background: #6c757d;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n \r\n &:hover {\r\n background: #5a6268;\r\n }\r\n}\r\n\r\n/* 滚动条样式 */\r\n.panel-content::-webkit-scrollbar {\r\n width: 6px;\r\n}\r\n\r\n.panel-content::-webkit-scrollbar-track {\r\n background: #f1f1f1;\r\n}\r\n\r\n.panel-content::-webkit-scrollbar-thumb {\r\n background: #c1c1c1;\r\n border-radius: 3px;\r\n}\r\n\r\n.panel-content::-webkit-scrollbar-thumb:hover {\r\n background: #a8a8a8;\r\n}\r\n</style>","<template>\r\n <div class=\"link-config-panel\">\r\n <div class=\"panel-header\">\r\n <h3>连线配置</h3>\r\n <button class=\"close-btn\" @click=\"$emit('close')\" title=\"关闭\">×</button>\r\n </div>\r\n \r\n <div class=\"panel-content\">\r\n <!-- 路径类型选择 -->\r\n <div class=\"config-section\">\r\n <h4>路径类型</h4>\r\n <div class=\"path-type-selector\">\r\n <label \r\n v-for=\"pathType in pathTypes\" \r\n :key=\"pathType.value\"\r\n class=\"path-type-option\"\r\n :class=\"{ active: config.pathType === pathType.value }\"\r\n >\r\n <input \r\n type=\"radio\" \r\n :value=\"pathType.value\" \r\n v-model=\"config.pathType\"\r\n @change=\"updateConfig\"\r\n />\r\n <div class=\"path-preview\">\r\n <svg width=\"60\" height=\"40\" viewBox=\"0 0 60 40\">\r\n <path \r\n :d=\"pathType.preview\" \r\n stroke=\"#3498db\" \r\n stroke-width=\"2\" \r\n fill=\"none\"\r\n />\r\n </svg>\r\n </div>\r\n <span>{{ pathType.name }}</span>\r\n </label>\r\n </div>\r\n </div>\r\n \r\n <!-- 基础样式配置 -->\r\n <div class=\"config-section\">\r\n <h4>基础样式</h4>\r\n <div class=\"config-row\">\r\n <label>颜色:</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"config.color\" \r\n @change=\"updateConfig\"\r\n class=\"color-input\"\r\n />\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>线宽:</label>\r\n <input \r\n type=\"range\" \r\n min=\"1\" \r\n max=\"5\" \r\n step=\"0.5\"\r\n v-model.number=\"config.width\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.width }}px</span>\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>虚线样式:</label>\r\n <select v-model=\"config.dashArray\" @change=\"updateConfig\" class=\"select-input\">\r\n <option :value=\"undefined\">实线</option>\r\n <option value=\"3,3\">短虚线</option>\r\n <option value=\"5,5\">中虚线</option>\r\n <option value=\"8,4\">长虚线</option>\r\n <option value=\"2,2,8,2\">点划线</option>\r\n </select>\r\n </div>\r\n </div>\r\n \r\n <!-- 路径特定配置 -->\r\n <div class=\"config-section\" v-if=\"config.pathType === 'bezier'\">\r\n <h4>贝塞尔曲线配置</h4>\r\n <div class=\"config-row\">\r\n <label>弯曲度:</label>\r\n <input \r\n type=\"range\" \r\n min=\"0.1\" \r\n max=\"1\" \r\n step=\"0.1\"\r\n v-model.number=\"config.bezierCurvature\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.bezierCurvature }}</span>\r\n </div>\r\n </div>\r\n \r\n <div class=\"config-section\" v-if=\"config.pathType === 'right-angle'\">\r\n <h4>直角连线配置</h4>\r\n <div class=\"config-row\">\r\n <label>偏移距离:</label>\r\n <input \r\n type=\"range\" \r\n min=\"10\" \r\n max=\"80\" \r\n step=\"5\"\r\n v-model.number=\"config.rightAngleOffset\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.rightAngleOffset }}px</span>\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"config.smoothCorners\" \r\n @change=\"updateConfig\"\r\n />\r\n 平滑转角\r\n </label>\r\n </div>\r\n \r\n <div class=\"config-row\" v-if=\"config.smoothCorners\">\r\n <label>转角半径:</label>\r\n <input \r\n type=\"range\" \r\n min=\"0\" \r\n max=\"15\" \r\n step=\"1\"\r\n v-model.number=\"config.cornerRadius\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.cornerRadius }}px</span>\r\n </div>\r\n </div>\r\n \r\n <!-- 箭头配置 -->\r\n <div class=\"config-section\">\r\n <h4>箭头配置</h4>\r\n <div class=\"config-row\">\r\n <label>\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"config.showArrow\" \r\n @change=\"updateConfig\"\r\n />\r\n 显示箭头\r\n </label>\r\n </div>\r\n \r\n <div v-if=\"config.showArrow\">\r\n <div class=\"config-row\">\r\n <label>箭头大小:</label>\r\n <input \r\n type=\"range\" \r\n min=\"4\" \r\n max=\"16\" \r\n step=\"1\"\r\n v-model.number=\"config.arrowSize\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.arrowSize }}px</span>\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>箭头颜色:</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"config.arrowColor\" \r\n @change=\"updateConfig\"\r\n class=\"color-input\"\r\n :placeholder=\"config.color\"\r\n />\r\n <button \r\n @click=\"config.arrowColor = config.color; updateConfig()\" \r\n class=\"sync-btn\"\r\n title=\"与线条颜色同步\"\r\n >\r\n 同步\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- 虚线动画配置 -->\r\n <div class=\"config-section\">\r\n <h4>虚线动画</h4>\r\n <div class=\"config-row\">\r\n <label>\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"config.enableDashAnimation\" \r\n @change=\"updateConfig\"\r\n />\r\n 启用虚线流动动画\r\n </label>\r\n </div>\r\n \r\n <div v-if=\"config.enableDashAnimation\">\r\n <div class=\"config-row\">\r\n <label>动画速度:</label>\r\n <input \r\n type=\"range\" \r\n min=\"0.2\" \r\n max=\"3\" \r\n step=\"0.2\"\r\n v-model.number=\"config.dashAnimationSpeed\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.dashAnimationSpeed }}s</span>\r\n </div>\r\n \r\n <div class=\"config-info\">\r\n <small>💡 虚线动画只在连线设置为虚线样式时显示</small>\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- 标签配置 -->\r\n <div class=\"config-section\">\r\n <h4>标签配置</h4>\r\n <div class=\"config-row\">\r\n <label>\r\n <input \r\n type=\"checkbox\" \r\n v-model=\"config.showLabels\" \r\n @change=\"updateConfig\"\r\n />\r\n 显示标签\r\n </label>\r\n </div>\r\n \r\n <div v-if=\"config.showLabels\">\r\n <div class=\"config-row\">\r\n <label>标签颜色:</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"config.labelColor\" \r\n @change=\"updateConfig\"\r\n class=\"color-input\"\r\n />\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>字体大小:</label>\r\n <input \r\n type=\"range\" \r\n min=\"8\" \r\n max=\"16\" \r\n step=\"1\"\r\n v-model.number=\"config.labelFontSize\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.labelFontSize }}px</span>\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- 父子关系样式 -->\r\n <div class=\"config-section\">\r\n <h4>父子关系样式</h4>\r\n <div class=\"config-row\">\r\n <label>颜色:</label>\r\n <input \r\n type=\"color\" \r\n v-model=\"config.parentChildStyle.color\" \r\n @change=\"updateConfig\"\r\n class=\"color-input\"\r\n />\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>线宽:</label>\r\n <input \r\n type=\"range\" \r\n min=\"1\" \r\n max=\"3\" \r\n step=\"0.5\"\r\n v-model.number=\"config.parentChildStyle.width\" \r\n @input=\"updateConfig\"\r\n class=\"range-input\"\r\n />\r\n <span class=\"value\">{{ config.parentChildStyle.width }}px</span>\r\n </div>\r\n \r\n <div class=\"config-row\">\r\n <label>虚线样式:</label>\r\n <select v-model=\"config.parentChildStyle.dashArray\" @change=\"updateConfig\" class=\"select-input\">\r\n <option :value=\"undefined\">实线</option>\r\n <option value=\"3,3\">短虚线</option>\r\n <option value=\"5,5\">中虚线</option>\r\n <option value=\"8,4\">长虚线</option>\r\n </select>\r\n </div>\r\n </div>\r\n \r\n <!-- 预设主题 -->\r\n <div class=\"config-section\">\r\n <h4>预设主题</h4>\r\n <div class=\"theme-selector\">\r\n <button \r\n v-for=\"(theme, name) in themes\" \r\n :key=\"name\"\r\n @click=\"applyTheme(name)\"\r\n class=\"theme-btn\"\r\n :class=\"{ active: isCurrentTheme(name) }\"\r\n >\r\n {{ getThemeName(name) }}\r\n </button>\r\n </div>\r\n </div>\r\n \r\n <!-- 操作按钮 -->\r\n <div class=\"config-section\">\r\n <div class=\"action-buttons\">\r\n <button @click=\"resetConfig\" class=\"reset-btn\">重置为默认</button>\r\n <button @click=\"exportConfig\" class=\"export-btn\">导出配置</button>\r\n <button @click=\"showImportDialog = true\" class=\"import-btn\">导入配置</button>\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- 导入配置对话框 -->\r\n <div v-if=\"showImportDialog\" class=\"import-dialog-overlay\" @click=\"showImportDialog = false\">\r\n <div class=\"import-dialog\" @click.stop>\r\n <h4>导入配置</h4>\r\n <textarea \r\n v-model=\"importText\" \r\n placeholder=\"粘贴配置JSON...\"\r\n class=\"import-textarea\"\r\n ></textarea>\r\n <div class=\"import-actions\">\r\n <button @click=\"importConfig\" class=\"confirm-btn\">导入</button>\r\n <button @click=\"showImportDialog = false\" class=\"cancel-btn\">取消</button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport { defineComponent, ref, computed } from 'vue';\r\nimport { LinkPathType, LinkThemes, useLinkConfig } from './LinkConfig';\r\n\r\nexport default defineComponent({\r\n name: 'LinkConfigPanel',\r\n emits: ['close', 'configChange'],\r\n setup(_, { emit }) {\r\n const { \r\n config, \r\n setTheme, \r\n updateConfig: updateLinkConfig, \r\n reset, \r\n exportConfig: exportLinkConfig, \r\n importConfig: importLinkConfig,\r\n themes \r\n } = useLinkConfig();\r\n \r\n const showImportDialog = ref(false);\r\n const importText = ref('');\r\n \r\n // 路径类型选项\r\n const pathTypes = [\r\n {\r\n value: LinkPathType.STRAIGHT,\r\n name: '直线',\r\n preview: 'M 10 20 L 50 20'\r\n },\r\n {\r\n value: LinkPathType.BEZIER,\r\n name: '贝塞尔曲线',\r\n preview: 'M 10 20 C 25 20 35 20 50 20'\r\n },\r\n {\r\n value: LinkPathType.RIGHT_ANGLE,\r\n name: '直角连线',\r\n preview: 'M 10 20 L 30 20 L 30 30 L 50 30'\r\n }\r\n ];\r\n \r\n // 主题名称映射\r\n const themeNames = {\r\n default: '默认',\r\n business: '商务',\r\n modern: '现代',\r\n minimal: '简约',\r\n colorful: '彩色'\r\n };\r\n \r\n const getThemeName = (name: string) => {\r\n return themeNames[name as keyof typeof themeNames] || name;\r\n };\r\n \r\n // 检查是否为当前主题\r\n const isCurrentTheme = (themeName: string) => {\r\n const theme = themes[themeName as keyof typeof themes];\r\n if (!theme) return false;\r\n \r\n return Object.keys(theme).every(key => {\r\n const themeValue = theme[key as keyof typeof theme];\r\n const configValue = config[key as keyof typeof config];\r\n \r\n if (typeof themeValue === 'object' && typeof configValue === 'object') {\r\n return JSON.stringify(themeValue) === JSON.stringify(configValue);\r\n }\r\n \r\n return themeValue === configValue;\r\n });\r\n };\r\n \r\n // 更新配置\r\n const updateConfig = () => {\r\n updateLinkConfig(config);\r\n emit('configChange', config);\r\n };\r\n \r\n // 应用主题\r\n const applyTheme = (themeName: string) => {\r\n setTheme(themeName as keyof typeof LinkThemes);\r\n emit('configChange', config);\r\n };\r\n \r\n // 重置配置\r\n const resetConfig = () => {\r\n reset();\r\n emit('configChange', config);\r\n };\r\n \r\n // 导出配置\r\n const exportConfig = () => {\r\n const configJson = exportLinkConfig();\r\n navigator.clipboard.writeText(configJson).then(() => {\r\n alert('配置已复制到剪贴板');\r\n }).catch(() => {\r\n // 降级方案:显示配置内容\r\n const textarea = document.createElement('textarea');\r\n textarea.value = configJson;\r\n document.body.appendChild(textarea);\r\n textarea.select();\r\n document.execCommand('copy');\r\n document.body.removeChild(textarea);\r\n alert('配置已复制到剪贴板');\r\n });\r\n };\r\n \r\n // 导入配置\r\n const importConfig = () => {\r\n try {\r\n const success = importLinkConfig(importText.value);\r\n if (success) {\r\n showImportDialog.value = false;\r\n importText.value = '';\r\n emit('configChange', config);\r\n alert('配置导入成功');\r\n } else {\r\n alert('配置格式错误,请检查JSON格式');\r\n }\r\n } catch (error) {\r\n alert('配置导入失败:' + error);\r\n }\r\n };\r\n \r\n return {\r\n config,\r\n pathTypes,\r\n themes,\r\n showImportDialog,\r\n importText,\r\n getThemeName,\r\n isCurrentTheme,\r\n updateConfig,\r\n applyTheme,\r\n resetConfig,\r\n exportConfig,\r\n importConfig\r\n };\r\n }\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.link-config-panel {\r\n width: 320px;\r\n background: white;\r\n border: 1px solid #ddd;\r\n border-radius: 8px;\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\r\n max-height: 80vh;\r\n overflow: hidden;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.panel-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 16px 20px;\r\n border-bottom: 1px solid #eee;\r\n background: #f8f9fa;\r\n \r\n h3 {\r\n margin: 0;\r\n font-size: 16px;\r\n font-weight: 600;\r\n color: #333;\r\n }\r\n \r\n .close-btn {\r\n background: none;\r\n border: none;\r\n font-size: 20px;\r\n cursor: pointer;\r\n color: #666;\r\n padding: 0;\r\n width: 24px;\r\n height: 24px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n border-radius: 4px;\r\n \r\n &:hover {\r\n background: #e9ecef;\r\n color: #333;\r\n }\r\n }\r\n}\r\n\r\n.panel-content {\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 16px 20px;\r\n}\r\n\r\n.config-section {\r\n margin-bottom: 24px;\r\n \r\n h4 {\r\n margin: 0 0 12px 0;\r\n font-size: 14px;\r\n font-weight: 600;\r\n color: #495057;\r\n }\r\n}\r\n\r\n.path-type-selector {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n.path-type-option {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n padding: 8px 12px;\r\n border: 2px solid #e9ecef;\r\n border-radius: 6px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n \r\n &:hover {\r\n border-color: #3498db;\r\n background: #f8f9ff;\r\n }\r\n \r\n &.active {\r\n border-color: #3498db;\r\n background: #e3f2fd;\r\n }\r\n \r\n input[type=\"radio\"] {\r\n display: none;\r\n }\r\n \r\n .path-preview {\r\n flex-shrink: 0;\r\n }\r\n \r\n span {\r\n font-size: 13px;\r\n color: #495057;\r\n }\r\n}\r\n\r\n.config-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n margin-bottom: 12px;\r\n \r\n label {\r\n font-size: 13px;\r\n color: #495057;\r\n min-width: 80px;\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n }\r\n \r\n .value {\r\n font-size: 12px;\r\n color: #6c757d;\r\n min-width: 40px;\r\n text-align: right;\r\n }\r\n}\r\n\r\n.color-input {\r\n width: 40px;\r\n height: 32px;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n padding: 0;\r\n \r\n &::-webkit-color-swatch-wrapper {\r\n padding: 0;\r\n }\r\n \r\n &::-webkit-color-swatch {\r\n border: none;\r\n border-radius: 3px;\r\n }\r\n}\r\n\r\n.range-input {\r\n flex: 1;\r\n height: 6px;\r\n background: #e9ecef;\r\n border-radius: 3px;\r\n outline: none;\r\n \r\n &::-webkit-slider-thumb {\r\n appearance: none;\r\n width: 16px;\r\n height: 16px;\r\n background: #3498db;\r\n border-radius: 50%;\r\n cursor: pointer;\r\n }\r\n \r\n &::-moz-range-thumb {\r\n width: 16px;\r\n height: 16px;\r\n background: #3498db;\r\n border-radius: 50%;\r\n cursor: pointer;\r\n border: none;\r\n }\r\n}\r\n\r\n.select-input {\r\n flex: 1;\r\n padding: 6px 8px;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n font-size: 13px;\r\n background: white;\r\n \r\n &:focus {\r\n outline: none;\r\n border-color: #3498db;\r\n }\r\n}\r\n\r\n.sync-btn {\r\n padding: 4px 8px;\r\n font-size: 11px;\r\n background: #f8f9fa;\r\n border: 1px solid #ddd;\r\n border-radius: 3px;\r\n cursor: pointer;\r\n \r\n &:hover {\r\n background: #e9ecef;\r\n }\r\n}\r\n\r\n.theme-selector {\r\n display: grid;\r\n grid-template-columns: 1fr 1fr;\r\n gap: 8px;\r\n}\r\n\r\n.theme-btn {\r\n padding: 8px 12px;\r\n font-size: 12px;\r\n background: #f8f9fa;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n \r\n &:hover {\r\n background: #e9ecef;\r\n border-color: #adb5bd;\r\n }\r\n \r\n &.active {\r\n background: #3498db;\r\n color: white;\r\n border-color: #3498db;\r\n }\r\n}\r\n\r\n.action-buttons {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n.reset-btn, .export-btn, .import-btn {\r\n padding: 8px 16px;\r\n font-size: 13px;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.reset-btn {\r\n background: #f8f9fa;\r\n color: #495057;\r\n \r\n &:hover {\r\n background: #e9ecef;\r\n }\r\n}\r\n\r\n.export-btn {\r\n background: #28a745;\r\n color: white;\r\n border-color: #28a745;\r\n \r\n &:hover {\r\n background: #218838;\r\n }\r\n}\r\n\r\n.import-btn {\r\n background: #17a2b8;\r\n color: white;\r\n border-color: #17a2b8;\r\n \r\n &:hover {\r\n background: #138496;\r\n }\r\n}\r\n\r\n.import-dialog-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 1000;\r\n}\r\n\r\n.import-dialog {\r\n background: white;\r\n border-radius: 8px;\r\n padding: 20px;\r\n width: 400px;\r\n max-width: 90vw;\r\n \r\n h4 {\r\n margin: 0 0 16px 0;\r\n font-size: 16px;\r\n color: #333;\r\n }\r\n}\r\n\r\n.import-textarea {\r\n width: 100%;\r\n height: 120px;\r\n padding: 8px;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n font-family: monospace;\r\n font-size: 12px;\r\n resize: vertical;\r\n \r\n &:focus {\r\n outline: none;\r\n border-color: #3498db;\r\n }\r\n}\r\n\r\n.import-actions {\r\n display: flex;\r\n gap: 8px;\r\n margin-top: 16px;\r\n justify-content: flex-end;\r\n}\r\n\r\n.confirm-btn {\r\n padding: 8px 16px;\r\n background: #28a745;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n \r\n &:hover {\r\n background: #218838;\r\n }\r\n}\r\n\r\n.cancel-btn {\r\n padding: 8px 16px;\r\n background: #6c757d;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n \r\n &:hover {\r\n background: #5a6268;\r\n }\r\n}\r\n\r\n/* 滚动条样式 */\r\n.panel-content::-webkit-scrollbar {\r\n width: 6px;\r\n}\r\n\r\n.panel-content::-webkit-scrollbar-track {\r\n background: #f1f1f1;\r\n}\r\n\r\n.panel-content::-webkit-scrollbar-thumb {\r\n background: #c1c1c1;\r\n border-radius: 3px;\r\n}\r\n\r\n.panel-content::-webkit-scrollbar-thumb:hover {\r\n background: #a8a8a8;\r\n}\r\n</style>","import type { App } from 'vue';\r\nimport Gantt from './components/gantt/Gantt.vue';\r\n\r\n// 导出组件\r\nexport { default as Gantt } from './components/gantt/Gantt.vue';\r\nexport { default } from './components/gantt/Gantt.vue';\r\n\r\n// 导出辅助组件\r\nexport { default as DatePicker } from './components/gantt/DatePicker.vue';\r\nexport { default as GanttThemeSelector } from './components/gantt/GanttThemeSelector.vue';\r\nexport { default as LanguageSelector } from './components/gantt/LanguageSelector.vue';\r\nexport { default as GanttConfigPanel } from './components/gantt/GanttConfigPanel.vue';\r\nexport { default as LinkConfigPanel } from './components/gantt/LinkConfigPanel.vue';\r\n\r\n// 导出类型定义\r\nexport type {\r\n DataConfig,\r\n StyleConfig,\r\n EventConfig,\r\n TaskHeader,\r\n LinkConfig,\r\n TaskLink,\r\n TaskDependency,\r\n ProgressUpdateDetail,\r\n LinkTypeColors,\r\n LinkTypeVisibility\r\n} from './components/gantt/Types';\r\n\r\n// 导出枚举\r\nexport { LinkType, LinkPathType } from './components/gantt/Types';\r\n\r\n// 导出主题系统\r\nexport { ganttThemes, type GanttTheme } from './components/gantt/themes/GanttThemes.ts';\r\n\r\n// 导出连线配置\r\nexport { LinkThemes, useLinkConfig, linkDataManager } from './components/gantt/LinkConfig';\r\n\r\n// 导出国际化系统\r\nexport { useI18n, setLocale, type Locale } from './components/gantt/i18n';\r\n\r\n// 导出 Store (可选,高级用户可能需要)\r\nexport { store, mutations } from './components/gantt/Store';\r\n\r\n// 支持 Vue.use() 安装\r\nexport const install = (app: App) => {\r\n app.component('Gantt', Gantt);\r\n app.component('Vue3Gantt', Gantt);\r\n};\r\n\r\n// 版本信息\r\nexport const version = '1.0.0';\r\n"],"names":["e","t","module","this","n","r","i","o","s","a","f","h","u","d","c","l","m","M","Y","p","v","D","w","g","y","L","_","require$$0","SetBarColorSymbol","AddRootTaskSymbol","Symbols","LinkType","LinkPathType","LinkThemes","LinkTypeConfig","LinkConfigManager","__publicField","reactive","stored","parsedConfig","error","themeName","newConfig","linkType","typeConfig","configJson","importedConfig","linkConfigManager","useLinkConfig","LinkDataManager","parsedDependencies","dependency","id","index","dep","taskId","sourceId","targetId","visited","recursionStack","hasCycle","nodeId","dependencies","result","linkDataManager","messages","currentLocale","ref","savedLocale","key","keys","value","k","setLocale","locale","getLocale","getLocales","useI18n","computed","_sfc_main$h","defineComponent","props","emit","getDayjsLocale","getWeekNames","dayjsLocale","weekNames","day","dayjs","getMonthName","month","selectDate","showDate","copyMinDate","copyMaxDate","fadeXType","nextMonthClick","prevMonthClick","showYear","showMonth","yearListRef","monthListRef","showCalendar","selectedDateText","calendarRef","inputRef","hoveredYearIndex","hoveredMonthIndex","hoveredDayIndex","yearList","monthList","rows","year","months","row","weekValue","addRowEmptyValue","addRowDayValue","keepDoubleDigit","number","splitDate","date","addStr","splitValue","initDatePicker","splitResult","dateObj","dateFormat","watchEffect","onBeforeMount","count","showDateValue","newValue","isMinLimitMonth","isMaxLimitMonth","prevMonth","nextMonth","resetSelectDate","dayValue","selectDay","days","week","weekStr","monthStr","selectMonth","type","daysInMonth","selectYear","openYearList","openMonthList","openCalendarList","clearDate","handleClickOutside","event","isYearHovered","isMonthHovered","isDayHovered","col","handleYearMouseEnter","handleYearMouseLeave","handleMonthMouseEnter","handleMonthMouseLeave","handleDayMouseEnter","handleDayMouseLeave","onMounted","onUnmounted","_hoisted_2","_hoisted_3","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_sfc_render","_ctx","_cache","$props","$setup","$data","$options","_createElementBlock","_createElementVNode","_hoisted_1","_withDirectives","$event","_vModelText","_openBlock","args","_createCommentVNode","_hoisted_4","_hoisted_5","_createVNode","_Transition","_normalizeClass","_toDisplayString","_hoisted_6","_createTextVNode","_hoisted_7","_withCtx","_Fragment","_renderList","weekName","dayIndex","_vShow","item","_sfc_main$g","splitPane","trigger","triggerLeftOffset","triggerBackGroud","lengthType","triggerDefaultColor","triggerMoveColor","paneLengthValue","triggerLengthValue","nextTick","handleMouseLeave","handleMouseOver","handleMouseDown","handleMouseMove","handleMouseUp","clientRect","paneLengthPercent","direction","_normalizeStyle","_sfc_main$f","resizing","resizingIndex","startX","startWidth","currentHeaderElement","getHeaderTitle","header","translationKey","startResize","target","rafId","deltaX","newWidth","cell","finalWidth","initialStore","store","mutations","monthHeaders","dayHeaders","tasks","taskHeaders","weekHeaders","hourHeaders","scale","mapFields","timelineCellCount","startGanttDate","endGanttDate","scrollFlag","mode","expandRow","rootTask","subTask","editTask","removeTask","barDate","task","sharedState","scrollTop","setScrollTop","setScrollFlag","useScrollState","_sfc_main$e","showRow","hover","addTips","removeTips","collapsedTasks","inject","addRootTask","hasChildren","currentId","isCollapsed","isLastChild","parentId","siblings","getAncestorLines","lines","treeLevel","currentLineLevel","path","currentTask","parent","node","nodeTreeLevel","toggleCollapse","setSubTask","setEditTask","setRemoveTask","checkField","property","watch","newId","headerIndex","rowHeight","level","_withModifiers","_sfc_main$d","TaskRow","hiddenTask","getAllCollapsedChildren","collapsedChildren","collectChildren","pid","child","childId","hiddenTaskIds","obj","filterTask","hiddenIds","allCollapsedIds","collapsedId","newVal","recursionRow","findRows","_resolveComponent","headers","_createBlock","_component_TaskRow","_sfc_main$c","TaskRecursionRow","taskContent","contentHeight","getRootNode","scroll","mouseover","syncScrollHeight","rightContent","handleResize","isBetween","_sfc_main$b","TaskHeader","TaskContent","setRootTask","_component_TaskHeader","_component_TaskContent","_sfc_main$a","toRefs","title","isToday","isoWeek","_sfc_main$9","bar","barHeight","oldBarDataX","oldBarWidth","barColor","isBarInteracted","themeVersion","isProgressDragging","progress","progressValue","setBarColor","emitProgressUpdate","newProgress","detail","hoverActive","hoverInactive","WeekEndColor","bgContent","bgSecondary","element","currentDate","setBarDate","setAllowChangeTaskDate","drowBar","barElement","dataX","fromPlanStartDays","spendDays","startGanttWeek","taskStartWeek","fromPlanStartWeeks","spendWeeks","fromPlanStartHours","spendHours","svg","SVG","borderColor","innerRect","outerRect","text","progressHandle","add","innerRectWidth","textBBox","handleWidth","handleHeight","handleX","handleY","lineX","getThemeColors","primary","primaryDark","primaryLight","guideLineEl","themeColors","line","handleElement","trianglePoints","currentHandleX","colors","interact","newLineX","x","currentX","alignedX","parentIdField","currentParentId","parentTask","parentStartX","ganttStartWeek","cellsMoved","daysOffset","hoursOffset","startWeek","endWeek","newParentStartDate","currentTaskId","getAllChildren","children","childTasks","widthCells","startCellIndex","endCellIndex","newStartDate","newEndDate","childOffset","ganttContainer","observer","onBeforeUnmount","onDeactivated","_sfc_main$8","componentKey","allTask","setExpandRow","Bar","_component_Bar","_sfc_main$7","links","getTaskPosition","svgBar","rightTableContent","barWidth","barRowRect","containerRect","relativeY","getChildIndex","getRowHeight","firstBarRow","alignToGridCenter","getChildRowCenter","parentY","childIndex","getSafeBypassY","childY","gap","minBypassY","getLinkTypeColor","getLinkStyle","generateLinkPath","source","linkId","pathType","generateParentChildPath","generateFinishToStartPath","generateStartToStartPath","generateFinishToFinishPath","generateStartToFinishPath","_linkId","startY","arrowSize","endX","endY","generateParentToChildPath","deltaY","generateBezierPath","generateRightAnglePath","_childIndex","bezierCurvature","curvature","cp1X","cp2X","bypassY","rightAngleOffset","smoothCorners","cornerRadius","midX","midX1","midX2","generateConnectionPath","connectionType","generateBezierConnectionPath","generateRightAngleConnectionPath","curvatureOffset","leftGap","leftOffsetX","leftCurvature","midY","rightGap","rightOffsetX","rightCurvature","crossCurvature","crossCp1X","crossCp2X","offset","crossMidX","generateArrowPoints","childPos","_linkType","tipX","tipY","baseX","baseY1","baseY2","generateDependencyArrowPoints","sourcePos","targetPos","arrowPoint1X","arrowPoint1Y","arrowPoint2X","arrowPoint2Y","isTaskCollapsed","updateLinks","newLinks","visibility","parentPos","parentPosWithId","childPosWithId","arrowPoints","shouldShowLinkType","sourcePosWithId","targetPosWithId","labelMap","resizeObserver","mutationObserver","container","shouldUpdate","mutation","attrName","_a","_b","isDashedLine","style","link","speed","adjustedSpeed","dashLength","sum","part","getDashAnimationStyle","linkConfig","_sfc_main$6","BarRecursionRow","TaskLinks","barContent","containerWidth","containerHeight","_component_TaskLinks","_component_BarRecursionRow","_sfc_main$5","TimelineHeader","TableContent","tableBar","scrollToToday","currentWeekStart","ganttWeekStart","_component_TimelineHeader","ganttThemes","_GanttThemeManager","themeId","theme","savedTheme","config","GanttThemeManager","ganttThemeManager","_sfc_main$4","isOpen","themes","currentTheme","locales","selectLocale","localeValue","updateLinkConfigManager","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","pathTypes","togglePanel","closePanel","selectTheme","selectPathType","updateLinkConfig","_hoisted_14","_hoisted_20","_hoisted_21","_hoisted_25","_hoisted_26","_hoisted_28","_hoisted_30","_hoisted_31","_hoisted_32","_hoisted_34","_hoisted_35","_hoisted_36","_hoisted_37","_hoisted_38","_hoisted_39","_hoisted_40","_hoisted_41","_hoisted_44","_hoisted_45","_hoisted_46","_hoisted_47","_hoisted_48","_hoisted_50","_hoisted_51","_hoisted_52","_hoisted_53","_hoisted_54","_hoisted_55","_hoisted_56","_hoisted_57","_hoisted_58","_hoisted_59","_hoisted_60","_hoisted_62","_hoisted_63","_hoisted_64","_hoisted_65","_hoisted_66","_hoisted_67","_hoisted_68","_hoisted_72","_hoisted_73","_hoisted_74","_hoisted_76","_hoisted_77","_hoisted_78","_hoisted_79","_hoisted_80","_hoisted_83","_hoisted_84","_hoisted_85","_hoisted_86","_hoisted_87","_hoisted_90","_hoisted_91","_hoisted_92","_hoisted_93","_hoisted_94","_hoisted_97","_hoisted_98","_hoisted_99","_hoisted_100","_hoisted_104","_hoisted_105","_hoisted_106","_hoisted_107","_hoisted_108","_hoisted_109","_hoisted_110","_hoisted_111","_hoisted_112","_hoisted_113","_hoisted_114","_hoisted_115","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_27","_hoisted_29","_hoisted_42","_hoisted_43","_hoisted_49","_vModelCheckbox","_hoisted_61","_hoisted_69","_hoisted_70","_hoisted_71","_hoisted_75","_hoisted_81","_hoisted_82","_hoisted_88","_hoisted_89","_hoisted_95","_hoisted_96","_hoisted_101","_hoisted_102","_hoisted_103","_hoisted_116","z","customParseFormat","_sfc_main$3","DatePicker","SplitPane","TaskTable","RightTable","GanttConfigPanel","dataSource","linkTypeVisibility","updateLinkVisibility","initData","buttonClass","startDate","minStartDate","maxStartDate","showStartDatePicker","selectedStartDate","endDate","minEndDate","maxEndDate","showEndDatePicker","selectedEndDate","allowChangeTaskDate","setTimeLineHeaders","start","end","current","endOfMonth","startOfMonth","caption","fullDate","monthCurrent","monthStart","monthEnd","weekCount","weekCurrent","weekStart","weekEnd","monthTitle","weekTitle","endOfEndDay","needsHourSuffix","FindAllParent","targetData","RecursionData","findResult","timeMode","_mode","barContentEl","confirmStart","confirmEnd","newMode","oldMode","oldStartDate","oldEndDate","newScale","newHeaders","newMonth","newDay","newWeek","newHour","newFields","newCount","handleProgressUpdate","customEvent","updateTimer","provide","newDependencies","onLinkConfigChange","linkTypeColors","_hoisted_33","_component_SplitPane","_component_DatePicker","styleConfig","_component_RightTable","_sfc_main$2","previewTheme","fileInput","availableThemes","toggleSelector","closeSelector","onCancelPreview","injectThemeStyles","pageElement","onPreviewTheme","exportConfig","blob","url","importConfig","handleFileImport","file","reader","content","existingStyle","cssRules","initTheme","_sfc_main$1","currentLocaleLabel","toggleDropdown","closeDropdown","_sfc_main","setTheme","reset","exportLinkConfig","importLinkConfig","showImportDialog","importText","themeNames","name","themeValue","configValue","textarea","_vModelRadio","applyTheme","getThemeName","install","app","Gantt","version"],"mappings":"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","x_google_ignoreList":[0,1,2,3,4,5,6,7,8,35,40]}