1111 html body {
1212 background : #fff ;
1313 }
14+
15+ .diff-added {
16+ background-color : #d4edda ;
17+ color : #155724 ;
18+ padding : 1px 2px ;
19+ border-radius : 2px ;
20+ }
21+
22+ .diff-removed {
23+ background-color : #f8d7da ;
24+ color : #721c24 ;
25+ padding : 1px 2px ;
26+ border-radius : 2px ;
27+ }
28+
29+ .old-value-cell ,
30+ .new-value-cell {
31+ overflow : hidden ;
32+ text-overflow : ellipsis ;
33+ max-width : 400px ;
34+ white-space : nowrap ;
35+ }
36+
37+ tr .open .old-value-cell ,
38+ tr .open .new-value-cell {
39+ overflow : visible ;
40+ white-space : normal ;
41+ }
1442 </style >
1543}
1644<div style =" background :#fff " >
2957 @foreach ( var record in Model)
3058 {
3159 var changes = JsonConverter .Deserialize <Easy .AuditTrail .FieldChange []>(record .Changes ?? " []" );
32- if ( changes.Length > 0 )
60+ for ( int i = 0 ; i < changes.Length; i++ )
3361 {
34- for (int i = 0 ; i < changes .Length ; i ++ )
35- {
36- var change = changes [i ];
37- < tr >
38- < td > @change .Field < / td >
39- < td class = " text-muted" > @change .OldValue < / td >
40- < td class = " text-success" > @change .NewValue < / td >
41- @if (i == 0 )
62+ var change = changes [i ];
63+ < tr >
64+ < td > @change .Field < / td >
65+ < td >
66+ @if (change .OldValue != null )
4267 {
43- < td rowspan = " @changes.Length" > @record .CreatebyName < / td >
44- < td rowspan = " @changes.Length" > @record .CreateDate .Value .ToString (" yyyy-MM-dd HH:mm:ss" )< / td >
68+ < div class = " old-value-cell" > @change .OldValue < / div >
4569 }
46- < / tr >
47- }
48- }
49- else
50- {
51- < tr >
52- < td colspan = " 3" >< em > No changes < / em >< / td >
53- < td > @record .IPAddress < / td >
54- < td > @record .CreatebyName < / td >
55- < td > @record .CreateDate .Value .ToString (" yyyy-MM-dd HH:mm:ss" )< / td >
70+ < / td >
71+ < td >
72+ @if (change .ChangeType == (int )Easy .AuditTrail .AuditChangeType .Added )
73+ {
74+ < span class = " label label-success" >
75+ < code class = " small" > @L (" Add" )< / code >
76+ < span class = " text-success" > @change .NewValue < / span >
77+ < / span >
78+ }
79+ else if (change .ChangeType == (int )Easy .AuditTrail .AuditChangeType .Deleted )
80+ {
81+ < span class = " label label-danger" >
82+ < code class = " small" > @L (" Delete" )< / code >
83+ < del class = " text-danger" > @change .NewValue < / del >
84+ < / span >
85+ }
86+ else
87+ {
88+ < div class = " new-value-cell" > @change .NewValue < / div >
89+ }
90+ < / td >
91+ @if (i == 0 )
92+ {
93+ < td rowspan = " @changes.Length" > @record .CreatebyName < / td >
94+ < td rowspan = " @changes.Length" > @record .CreateDate .Value .ToString (" yyyy-MM-dd HH:mm:ss" )< / td >
95+ }
5696 < / tr >
5797 }
5898 }
63103 {
64104 await Html .Pagin ((ZKEACMS .Pagin )ViewBag .Pagin );
65105 }
66- < / div >
106+ < / div >
107+ @using (Script .AtFoot ())
108+ {
109+ < script type = " text/javascript" >
110+ function diffText (oldText , newText ) {
111+ const oldTokens = tokenizeText (oldText );
112+ const newTokens = tokenizeText (newText );
113+
114+ const m = oldTokens .length ;
115+ const n = newTokens .length ;
116+
117+ const dp = Array (m + 1 ).fill ().map (() => Array (n + 1 ).fill (0 ));
118+
119+ for (let i = 1 ; i <= m ; i ++ ) {
120+ for (let j = 1 ; j <= n ; j ++ ) {
121+ if (oldTokens [i - 1 ] == = newTokens [j - 1 ]) {
122+ dp [i ][j ] = dp [i - 1 ][j - 1 ] + 1 ;
123+ } else {
124+ dp [i ][j ] = Math .max (dp [i - 1 ][j ], dp [i ][j - 1 ]);
125+ }
126+ }
127+ }
128+
129+ let i = m , j = n ;
130+ const oldResult = [];
131+ const newResult = [];
132+
133+ while (i > 0 || j > 0 ) {
134+ if (i > 0 && j > 0 && oldTokens [i - 1 ] == = newTokens [j - 1 ]) {
135+ oldResult .unshift ({ text : oldTokens [i - 1 ], type : 'same' });
136+ newResult .unshift ({ text : newTokens [j - 1 ], type : 'same' });
137+ i -- ;
138+ j -- ;
139+ } else if (j > 0 && (i == = 0 || dp [i ][j - 1 ] >= dp [i - 1 ][j ])) {
140+ newResult .unshift ({ text : newTokens [j - 1 ], type : 'added' });
141+ j -- ;
142+ } else if (i > 0 && (j == = 0 || dp [i ][j - 1 ] < dp [i - 1 ][j ])) {
143+ oldResult .unshift ({ text : oldTokens [i - 1 ], type : 'removed' });
144+ i -- ;
145+ }
146+ }
147+
148+ return {
149+ oldResult : mergeAdjacentTokens (oldResult ),
150+ newResult : mergeAdjacentTokens (newResult )
151+ };
152+ }
153+
154+ function tokenizeText (text ) {
155+ const tokenRegex = / (\s + | [^ \w \s ]| \w + )/ g ;
156+ const tokens = [];
157+ let match ;
158+
159+ while ((match = tokenRegex .exec (text )) != = null ) {
160+ tokens .push (match [0 ]);
161+ }
162+
163+ return tokens ;
164+ }
165+
166+ function mergeAdjacentTokens (tokens ) {
167+ if (tokens .length == = 0 ) return [];
168+
169+ const result = [];
170+ let currentToken = { .. .tokens [0 ] };
171+
172+ for (let i = 1 ; i < tokens .length ; i ++ ) {
173+ if (tokens [i ].type == = currentToken .type ) {
174+ currentToken .text += tokens [i ].text ;
175+ } else {
176+ result .push (currentToken );
177+ currentToken = { .. .tokens [i ] };
178+ }
179+ }
180+
181+ result .push (currentToken );
182+ return result ;
183+ }
184+
185+ function tokensToHtml (tokens ) {
186+ return tokens .map (token => {
187+ let className = '' ;
188+ switch (token .type ) {
189+ case 'added' :
190+ return `< span class = " diff-added" > ${token .text }< / span > `;
191+ case 'removed' :
192+ return `< span class = " diff-removed" > ${token .text }< / span > `;
193+ case 'same' :
194+ return `< span > ${token .text }< / span > `;
195+ default :
196+ return `< span > ${token .text }< / span > `;
197+ }
198+ }).join ('' );
199+ }
200+
201+ $(function () {
202+ $(" tr" ).click (function () {
203+ $(this ).toggleClass (" open" );
204+ });
205+ $('tr' ).each (function () {
206+ const oldValueCell = $(this ).find ('.old-value-cell' );
207+ const newValueCell = $(this ).find ('.new-value-cell' );
208+
209+ const oldText = $.trim (oldValueCell .html ());
210+ const newText = $.trim (newValueCell .html ());
211+ if (oldText && newText ) {
212+ const diffResult = diffText (oldText , newText );
213+ newValueCell .html (tokensToHtml (diffResult .newResult ));
214+ oldValueCell .html (tokensToHtml (diffResult .oldResult ));
215+ }
216+ });
217+ });
218+ < / script >
219+ }
0 commit comments