自定義模板中數據標籤

筆記
數據標籤(DataTag)只是一種辨識度比較高的文本字符串,樣式完全由開發人員自己說了算。比如這樣的數據標籤“【##日期$$】”,編寫代碼openDataTag("【##日期$$】")即可返回數據標籤對象,進而可以對此數據標籤填充數據或設置樣式等操作。

在實際的Word文檔開發中,經常需要自動填充數據到Word模板中,以生成動態的Word文檔,那麼應該如何使用PageOffice概念中的數據標籤(DataTag)編輯製作Word模板呢?

方法一:直接打開Word文件插入數據標籤

假如使用數據標籤(DataTag)來定義模板中動態填充數據的位置,那麼直接打開一個Word文件,在其中添加自定義特殊格式的文本即可製作word模板。例如,打開一個Word文件後,可以直接插入“{部門名}”、“{姓名}”、“【時間】”等類似的、辨識度很高的、特殊格式的文本。效果如下圖所示:
image

Word模板中需要多少個動態填充數據的位置,就在Word文件中相應的位置插入多少數據標籤即可。這種製作word模板的方法操作簡單,但是常用於開發新項目時定義Word模板,開發人員知道在Word模板的哪個位置需要插入哪個數據,把所有需要動態填充數據的數據標籤插入到Word中,就可以編程調用PageOffice接口給這些數據標籤填充數據,就比如操作上面的“【時間】”,代碼這樣寫openDataTag("【時間】").setvalue("當前時間"),就可以把當前時間賦值給數據標籤。其他的數據標籤也是相同的處理方式,但是項目發佈後,如果用戶的需求發生變動,希望修改Word模板或創建新的Word模板,用戶怎麼知道如何製作word模板呢?如何插入和命名數據標籤?哪些數據標籤與哪些系統數據是對應的呢?

方法二:給用戶提供自定義模板功能

開發人員可以給系統增加一個模板管理的模塊,擁有模板管理權限的用戶可以查看系統中的模板列表,可以新增、刪除和在線編輯模板,開發人員通過程序定義好編輯某種模板時可能用到的所有數據標籤,用戶自定義編輯Word模板時可以把全部數據標籤插入到模板的相應位置,也可以根據實際需求只使用部分數據標籤,但Word模板中用到的數據標籤總是開發人員定義的數據標籤集合的子集,這樣一來就能實現開發人員與用戶的分工合作。

比如說web系統中合同模板的製作,合同模板中可能會用到的數據有:甲方、乙方、擔保人、合同編號、合同日期,那麼開發人員可以編寫代碼定義好這些數據標籤,讓用戶在線打開編輯模板時,只能在Word模板中插入這些系統定義好的數據標籤,這樣做不但實現了開發人員與用戶的一種約定,而且規範了用戶的操作。

1. 開發人員編寫代碼定義好編輯模板時所有可用的數據標籤:
開發人員調用PageOffice提供的defineDataTag方法定義用戶編輯模板時所有可用的數據標籤。數據標籤的格式沒有特殊要求,下面示例代碼中分別使用了英文中括號和中文中括號(也可以使用其他符號),是爲了便於用戶直觀的查看Word模板中哪些位置是插入的數據標籤,也是爲了通過程序給數據標籤動態賦值的時候與普通的正文內容做區別處理。

WordDocument doc = new WordDocument();
doc.getTemplate().defineDataTag("{ 甲方 }");
doc.getTemplate().defineDataTag("{ 乙方 }");
doc.getTemplate().defineDataTag("{ 擔保人 }");
doc.getTemplate().defineDataTag("【 合同日期 】");
doc.getTemplate().defineDataTag("【 合同編號 】");

2. 用戶編輯模板時,直接使用開發人員定義好的數據標籤來製作模板:
由於用戶操作習慣和Web系統界面風格的不同,開發人員可以根據實際需求在自己的Web項目中選擇實現下面的一種編輯模板效果。

用戶在線編輯模板效果1: 把數據標籤列表和Word文件編輯界面,一左一右放到同一個頁面窗口中,用戶在右側Word文件中點擊定位光標到需要插入數據的位置,然後在左側選擇相應的數據標籤,添加到當前位置即可。比如下圖所示:在Word中需要插入“乙方”的位置點擊一下鼠標,然後在左側“待添加標籤”中點擊數據標籤“{ 乙方 }”的添加按鈕,“{ 乙方 }”標籤就被插入到了Word光標所在位置。
image
此效果所用到的html、css和javascript代碼:

    <style>
        body {
            margin: 0;
            padding: 0;
            display: flex;
        }
		div{
			 box-sizing: border-box; 
		}
        #left-container {
            width: 360px;
            display: flex;
            flex-direction: column;
            border-right: 2px solid #ccc;
            padding: 20px;
            overflow: auto;
            font-size: 12px;
            height: 90vh;
        }
        #right-container {
            flex: 1;
            padding: 0px;
            height: 95vh;
        }
        #podiv{
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }
        #left-title{
            text-align: center;
            font-size: 16px;
            padding-bottom: 10px;
            margin-bottom: 10px;
            border-bottom: solid 1px #ccc;
        }

        .input-group {
            margin-bottom: 20px;
            display: flex;
            align-items: center;
        }
        input[type="text"] {
            width: 70%;
            padding: 10px;
            margin-top: 5px;
            box-sizing: border-box;
            border: 1px solid #ccc;
            border-radius: 5px;
            font-size: 12px;
            outline: none;
        }

        input[type="submit"] {
            width: 80px;
            padding: 10px;
            margin-top: 5px;
            margin-left: 10px;
            box-sizing: border-box;
            border: none;
            border-radius: 5px;
            background-color: #4E6EF2;
            color: white;
            font-size: 12px;
            outline: none;
            cursor: pointer;
        }
        /* 表格樣式 */
        table {
            border-collapse: collapse;
            width: 100%;
        }

        th, td {
            padding: 8px;
            text-align: left;
            border-bottom: 1px solid #ddd;
        }

        th {
            position: sticky;
            top: 0;
            background-color: #f2f2f2;
            z-index: 1;
        }

        /* 容器樣式 */
        .container {
            height: 600px;
            overflow: auto;
            border: solid 1px #ccc;
            scrollbar-width: thin;
            scrollbar-color: #888 #f2f2f2;
        }

        /* 滾動條樣式 */
        .container::-webkit-scrollbar {
            width: 8px;
        }

        .container::-webkit-scrollbar-track {
            background: #f2f2f2;
        }

        .container::-webkit-scrollbar-thumb {
            background-color: #888;
            border-radius: 4px;
        }

        .container::-webkit-scrollbar-thumb:hover {
            background-color: #555;
        }
        .delete-button {
            padding: 6px 6px;
            border: none;
            border-radius: 5px;
            background-color: #f44336;
            color: white;
            font-size: 12px;
            cursor: pointer;
        }
        .delete-button:hover {
            background-color: #d32f2f;
        }

        .normal-button {
            padding: 6px 6px;
            border: none;
            border-radius: 5px;
            background-color: #4E7EFF;
            color: white;
            font-size: 12px;
            cursor: pointer;
        }
        .normal-button:hover {
            background-color: #4E6EF2;
        }

        .locate-button {
            padding: 6px 6px;
            border: none;
            border-radius: 5px;
            background-color: #0abb87;
            color: white;
            font-size: 12px;
            cursor: pointer;
        }
        .locate-button:hover {
            background-color: #0a9966;
        }
    </style>
    <script>
        var definedDataTagJson = '';
        var isFromStart = false;
        var lastOpTag = '';

        function OnPageOfficeCtrlInit() {
            pageofficectrl.AddCustomToolButton("保存", "Save", 1);
        }
        function Save() {
            pageofficectrl.WebSave();
        }

        //加載數據
        function loadData() {
            var kWord = document.getElementById("inputKey").value;
            searchDataTag(definedDataTagJson, kWord);
            return;
        }

        //加載上方數據列表
        function searchDataTag(dtDefinedJson, s){
            var tb1 = document.getElementById("tagTable");
            var rCount = tb1.rows.length;
            for (var i = 1; i < rCount; i++) {
                tb1.deleteRow(1);
            }

            if('' == dtDefinedJson) dtDefinedJson = '[]';
            let definedDataTagObj = JSON.parse(dtDefinedJson);

            var oTable = document.getElementById("tagTable");
            var tbodyObj = oTable.tBodies[0];
            for(let key in definedDataTagObj ){
                let dtName = definedDataTagObj[key].name;
                if (dtName.toLocaleLowerCase().indexOf(s.toLocaleLowerCase()) > -1) {
                    var oTr = tbodyObj.insertRow();
                    var oTd = oTr.insertCell();
                    oTd.innerHTML = dtName;
                    oTd.title = dtName;
                    oTd = oTr.insertCell();
                    oTd.innerHTML = '<button class="delete-button" onclick="deleteTag(\''+ dtName +'\')">刪除</button> <button class="locate-button" onclick="locateTag(\''+ dtName +'\')" >定位</button> <button class="normal-button" onclick="addTag(\''+ dtName +'\');">添加</button> ';
                }
            }
        }

        function addTag(tagName) {
            pageofficectrl.word.SetTextToSelection(tagName);
        }

        function locateTag(tagName){
            pageofficectrl.word.SelectionCollapse(0);

            if(isFromStart){
                if(lastOpTag == tagName){
                    pageofficectrl.word.HomeKey(6);
                }

                isFromStart = false;
            }

            if(!pageofficectrl.word.FindNextText(tagName)){
                alert('已經搜索到文檔末尾。');
                isFromStart = true;
            }

            lastOpTag = tagName;
        }

        function deleteTag(tagName){
            let selectText = pageofficectrl.word.GetTextFromSelection();
            if(tagName != selectText){
                alert('請先執行‘'+tagName+'’的定位操作,然後再刪除。');
            }else{
                pageofficectrl.word.SetTextToSelection('');
            }
        }

        function AfterDocumentOpened() {
            definedDataTagJson = pageofficectrl.word.DataTagsDefinedAsJson;
            loadData();
        }

    </script> 


    <div id="left-container">
        <div id="left-title">定義數據標籤</div>
        <div class="input-group">
            <span style="font-size: 14px;">待添加標籤:</span><input type="text" id="inputKey" oninput="loadData();" placeholder="請輸入數據標籤關鍵字搜索">
        </div>
        <div class="container">
            <table id="tagTable">
                <thead>
                <tr>
                    <th>數據標籤</th>
                    <th style="width: 130px;">操作</th>
                </tr>
                </thead>
                <tbody>
                <!-- 數據行 -->
                </tbody>
            </table>
        </div>

    </div>

用戶在線編輯模板效果2: 編輯模板的界面只顯示Word文件,添加數據標籤時,彈出一個非模態的數據標籤選擇框,然後用戶就可以在Word文件中點擊定位光標到需要插入數據的位置,再點擊數據標籤選擇框中的數據標籤進行添加。比如下圖所示:點擊“定義數據標籤”按鈕,彈出數據標籤選擇框,然後定位光標到Word中需要插入“乙方”的位置,然後點擊數據標籤“{ 乙方 }”的添加按鈕,“{ 乙方 }”標籤就被插入到了Word光標所在位置。
image
此效果所用到的數據標籤選擇框DataTagDlg.htm的頁面代碼:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>定義數據標籤</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            font-size:10px;
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100%;
            background-color: #f5f5f5;
        }

        .container {
            display: flex;
            width: 99%;
            max-width: 100%;
        }

        .left, .right {
            background-color: #fff;
            padding:0 10px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
        }
        .left{
            flex-grow: 7;
        }
        .right {
            flex-grow: 3;
        }

        h5 {
            margin-bottom: 10px;
        }

        input[type="text"], textarea {
            width: 90%;
            padding: 10px;
            margin-bottom: 15px;
            border-radius: 5px;
            border: 1px solid #ccc;
        }

        /* 表格樣式 */
        table {
            table-layout: fixed;/* 設置表格的佈局爲固定佈局 */
            border-collapse: collapse;
            width: 100%;
        }

        th, td {
            padding: 8px;
            text-align: left;
            border-bottom: 1px solid #ddd;
            white-space: nowrap; /* 強制單元格內文字不換行 */
            overflow: hidden; /* 隱藏超出單元格的文字 */
            text-overflow: ellipsis; /* 顯示省略號 */
        }

        th {
            position: sticky;
            top: 0;
            background-color: #f2f2f2;
            z-index: 1;
        }

        /* 容器樣式 */
        .tbcontainer {
            height: 300px;
            overflow: auto;
            border: solid 1px #ccc;
            scrollbar-width: thin;
            scrollbar-color: #888 #f2f2f2;
        }

        /* 滾動條樣式 */
        .tbcontainer::-webkit-scrollbar {
            width: 8px;
        }

        .tbcontainer::-webkit-scrollbar-track {
            background: #f2f2f2;
        }

        .tbcontainer::-webkit-scrollbar-thumb {
            background-color: #888;
            border-radius: 4px;
        }

        .tbcontainer::-webkit-scrollbar-thumb:hover {
            background-color: #555;
        }
        .delete-button {
            padding: 3px 3px;
            border: none;
            border-radius: 3px;
            background-color: #f44336;
            color: white;
            font-size: 10px;
            cursor: pointer;
        }
        .delete-button:hover {
            background-color: #d32f2f;
        }

        .normal-button {
            padding: 3px 3px;
            border: none;
            border-radius: 3px;
            background-color: #4E7EFF;
            color: white;
            font-size: 10px;
            cursor: pointer;
        }
        .normal-button:hover {
            background-color: #4E6EF2;
        }

        .locate-button {
            padding: 3px 3px;
            border: none;
            border-radius: 3px;
            background-color: #0abb87;
            color: white;
            font-size: 10px;
            cursor: pointer;
        }
        .locate-button:hover {
            background-color: #0a9966;
        }

        /* 定義信息提示條的樣式 */
        .alert-container {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            z-index: 9999;
        }

        .alert-bar {
            padding: 10px;
            border-bottom: solid 1px #f8a2a6;
            background-color: #f8d7da;
            color: #721c24;
            font-weight: bold;
            text-align: center;
            margin-bottom: 1px;
            opacity: 1;
            transition: all 0.5s ease;
        }

        .alert-bar.hide {
            opacity: 0;
            transform: translateY(-50px);
        }
    </style>
</head>
<body>
<!-- 定義信息提示條的HTML結構 -->
<div id="alert-container" class="alert-container"></div>

<div class="container">
    <div class="left">
        <h5>待添加標籤:</h5>
        <input type="text" id="inputKey" oninput="loadData();" placeholder="請輸入數據標籤關鍵字搜索" />
        <div class="tbcontainer">
            <table id="tagTable">
                <thead>
                <tr>
                    <th>數據標籤</th>
                    <th style="width: 110px;">操作</th>
                </tr>
                </thead>
                <tbody>
                <!-- 大量的數據行 -->
                <!--<tr>-->
                <!--<td>AAA</td>-->
                <!--<td>BBB</td>-->
                <!--<td><button class="normal-button">添加</button></td>-->
                <!--</tr>-->

                </tbody>
            </table>
        </div>
    </div>

</div>

<script>
    let definedDataTagJson = window.external.UserParams;

    var isFromStart = false;
    var lastOpTag = '';

    //加載數據
    function loadData() {
        var kWord = document.getElementById("inputKey").value;
        searchDataTag(definedDataTagJson, kWord);
        return;
    }

    //加載上方數據列表
    function searchDataTag(dtDefinedJson, s){
        var tb1 = document.getElementById("tagTable");
        var rCount = tb1.rows.length;
        for (var i = 1; i < rCount; i++) {
            tb1.deleteRow(1);
        }

        if('' == dtDefinedJson) dtDefinedJson = '[]';
        let definedDataTagObj = JSON.parse(dtDefinedJson);

        var oTable = document.getElementById("tagTable");
        var tbodyObj = oTable.tBodies[0];
        for(let key in definedDataTagObj ){
            let dtName = definedDataTagObj[key].name;
            if (dtName.toLocaleLowerCase().indexOf(s.toLocaleLowerCase()) > -1) {
                var oTr = tbodyObj.insertRow();
                var oTd = oTr.insertCell();
                oTd.innerHTML = dtName;
                oTd.title = dtName;
                oTd = oTr.insertCell();
                oTd.innerHTML = '<button class="delete-button" onclick="deleteTag(\''+ dtName +'\')">刪除</button> <button class="locate-button" onclick="locateTag(\''+ dtName +'\')" >定位</button> <button class="normal-button" onclick="addTag(\''+ dtName +'\');">添加</button> ';
            }
        }
    }

    function addTag(tagName) {
        pageofficectrl.word.SetTextToSelection(tagName);
    }

    function locateTag(tagName){
        pageofficectrl.word.SelectionCollapse(0);

        if(isFromStart){
            if(lastOpTag == tagName){
                pageofficectrl.word.HomeKey(6);
            }

            isFromStart = false;
        }

        if(!pageofficectrl.word.FindNextText(tagName)){
            showAlert('已經搜索到文檔末尾。');
            isFromStart = true;
        }

        lastOpTag = tagName;
    }

    function deleteTag(tagName){
        let selectText = pageofficectrl.word.GetTextFromSelection();
        if(tagName != selectText){
            showAlert('請先執行‘'+tagName+'’的定位操作,然後再刪除。');
        }else{
            pageofficectrl.word.SetTextToSelection('');
        }
    }

    // 自定義alert提示條
    function showAlert(message) {
        var alertBar = document.createElement('div');
        alertBar.className = 'alert-bar';
        alertBar.textContent = message;

        var alertContainer = document.getElementById('alert-container');
        alertContainer.insertBefore(alertBar, alertContainer.firstChild);
        alertBar.style.animation = 'slideDown 0.5s ease-out';

        setTimeout(function() {
            alertBar.classList.add('hide');
            setTimeout(function() {
                alertContainer.removeChild(alertBar);
            }, 500);
        }, 2000);
    }

    loadData();
</script>
</body>
</html>

自定義工具欄“定義數據標籤”按鈕的相關代碼:

function Save() {
    pageofficectrl.WebSave();
}

function ShowDefineDataTags() {
    var dtDefinedJson = pageofficectrl.word.DataTagsDefinedAsJson;
    pageofficectrl.ShowHtmlModelessDialog("DataTagDlg.jsp", dtDefinedJson, "left=300px;top=390px;width=400px;height=410px;frame:no;");
}

function OnPageOfficeCtrlInit() {
    pageofficectrl.AddCustomToolButton("保存", "Save", 1);
    pageofficectrl.AddCustomToolButton("定義數據標籤", "ShowDefineDataTags", 20);
}

在最終需要動態填充數據到word模板中生成正式合同文件時,開發人員只管編寫代碼給所有的數據標籤賦值即可,無需關心用戶自定義的word模板中到底使用了哪些數據標籤,因爲那些沒有使用的數據標籤會被PageOffice自動忽略掉;而最終用戶也可以根據自己的實際需要定義好word模板中各項數據及其位置,無需關心數據從哪裏來,也不用事事都與開發人員溝通,當業務需求發生簡單的變化時,可以自主修改word模板來滿足新的需求。這樣一來,不管是用戶還是開發人員,都在一定程度上從這種紛雜多變的業務需求中解脫出來。

參考鏈接:用戶自定義模板中數據標籤

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章